小菜鸟

java菜鸟号正在起航

排序算法全解析:从原理到应用

排序是数据处理中的基础操作,根据数据是否全部在内存中,分为内排序(全在内存)和外排序(需内外存交换)。内排序是日常开发的重点,按策略可分为插入排序、交换排序、选择排序、归并排序等。本文详细解析各类内排序算法的原理、实现、性能及适用场景。

排序算法基础概念

内排序与外排序

  • 内排序:待排序数据全部在内存中,无需磁盘 IO,如插入排序、快速排序等。
  • 外排序:数据量超过内存容量,需分批加载到内存排序,再合并结果(如归并排序的外部实现)。

关键评价指标

  • 时间复杂度:算法执行时间随数据规模n的增长趋势(关注最坏、平均、最好情况)。
  • 空间复杂度:算法所需额外存储空间(如归并排序需 O (n) 辅助空间)。
  • 稳定性:排序后,相等元素的相对顺序是否保持不变(如[2, 2*]排序后仍为[2, 2*]则稳定)。

插入排序:逐步构建有序序列

插入排序的核心思想:将待排序元素逐个插入到已排序部分的正确位置,使已排序部分始终有序。

1. 直接插入排序

基本思想
  • 将数组分为 “已排序区” 和 “未排序区”,初始已排序区只有第一个元素。
  • 从第二个元素开始,依次将未排序区的元素插入到已排序区的合适位置(比它大的元素后移)。
示例步骤(以[67, 67, 14, 52, 29, 9, 90, 54, 87, 71]为例):
  1. 初始:[67](已排序),[67, 14, 52, 29, 9, 90, 54, 87, 71](未排序)。
  2. 插入 67:已排序区变为[67, 67]
  3. 插入 14:14 < 67,67 和 67 后移,已排序区变为[14, 67, 67]
  4. 依次插入剩余元素,最终得到有序数组。
代码实现
阅读全文 »

MyBatis 基础配置与执行流程全解析(从入门到实践)

MyBatis 作为 Java 生态中轻量级且灵活的持久层框架,其核心是通过配置文件解耦 SQL 与业务代码,同时简化 JDBC 冗余操作。配置细节优化、执行流程原理、工程化实践常见问题排查,帮助你彻底掌握 MyBatis 的基础使用与核心逻辑。

配置文件:从基础到优化

MyBatis 的配置体系分为「全局配置文件(mybatis-config.xml)」和「映射文件(XxxMapper.xml)」,两者分工明确 —— 全局配置负责框架级参数,映射文件负责业务 SQL 与实体映射。

全局配置文件(mybatis-config.xml)深度解读

(1)全局配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 1. 引入外部属性文件(解耦数据库配置,支持多环境切换) -->
    <properties resource="db.properties">
        <!-- 可选:默认值(外部文件未定义时生效) -->
        <property name="jdbc.username" value="default_root"/>
    </properties>

    <!-- 2. 全局设置(控制MyBatis核心行为,必配优化项) -->
    <settings>
        <!-- 开启驼峰命名自动映射(数据库字段user_name → Java属性userName) -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启SQL日志打印(开发环境调试用,生产环境可关闭或改用SLF4J) -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- 开启二级缓存(提升查询性能,需配合映射文件<cache>使用) -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 关闭激进延迟加载(按需加载关联数据,避免冗余查询) -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    <!-- 3. 类型别名(简化映射文件中类名书写,避免全类名冗余) -->
    <typeAliases>
        <!-- 方式1:单个类指定别名(alias可自定义,不写则默认类名首字母小写) -->
        <typeAlias type="com.zhanghe.study.mybatis.model.User" alias="User"/>
        <!-- 方式2:批量扫描包(推荐!包下所有类默认别名为类名首字母小写,如User→user) -->
        <package name="com.zhanghe.study.mybatis.model"/>
    </typeAliases>

    <!-- 4. 数据库环境配置(支持多环境,default指定默认环境) -->
    <environments default="development">
        <!-- 开发环境 -->
        <environment id="development">
            <!-- 事务管理器:JDBC(依赖数据库原生事务)/ MANAGED(交给容器如Spring管理) -->
            <transactionManager type="JDBC"/>
            <!-- 数据源:POOLED(连接池,生产推荐)/ UNPOOLED(无池化)/ JNDI(容器数据源) -->
            <dataSource type="POOLED">
                <!-- 引用外部properties文件中的配置(${key}) -->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
                <!-- 连接池优化参数 -->
                <property name="poolMaximumActiveConnections" value="20"/> <!-- 最大活跃连接数 -->
                <property name="poolMaximumIdleConnections" value="5"/>    <!-- 最大空闲连接数 -->
            </dataSource>
        </environment>
        <!-- 生产环境(可单独配置,切换时修改default值) -->
        <environment id="production">
            <transactionManager type="MANAGED"/>
            <dataSource type="POOLED">
                <property name="driver" value="${prod.jdbc.driver}"/>
                <property name="url" value="${prod.jdbc.url}"/>
                <property name="username" value="${prod.jdbc.username}"/>
                <property name="password" value="${prod.jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 5. 映射文件扫描(告诉MyBatis去哪里找SQL映射) -->
    <mappers>
        <!-- 方式1:单个映射文件(resource路径基于classpath,如src/main/resources/mapper/UserMapper.xml) -->
        <mapper resource="mapper/UserMapper.xml"/>
        <!-- 方式2:批量扫描Mapper接口(推荐!需满足「接口与映射文件同包同名」) -->
        <!-- <package name="com.zhanghe.study.mybatis.mapper"/> -->
    </mappers>
</configuration>
(2)外部属性文件(db.properties

将数据库配置单独抽取到 src/main/resources/db.properties,便于维护和环境切换:

# 开发环境
jdbc.driver=com.mysql.cj.jdbc.Driver  # MySQL 8.0+需用cj驱动,5.x用com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

# 生产环境
prod.jdbc.driver=com.mysql.cj.jdbc.Driver
prod.jdbc.url=jdbc:mysql://192.168.1.100:3306/prod_db?useSSL=true&serverTimezone=UTC
prod.jdbc.username=prod_user
prod.jdbc.password=prod_123456

映射文件(UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace必须与Mapper接口全类名一致(绑定接口) -->
<mapper namespace="com.zhanghe.study.mybatis.mapper.UserMapper">
    <!-- 可选:开启当前Mapper的二级缓存(需全局settings中cacheEnabled=true) -->
    <cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>

    <!-- 结果映射(ResultMap):解决「表字段与实体属性名不一致」或「复杂关联查询」 -->
    <!-- 若开启驼峰映射(mapUnderscoreToCamelCase=true),简单场景可省略ResultMap -->
    <resultMap id="UserResultMap" type="User"> <!-- type用别名(全局配置中定义) -->
        <id column="user_id" property="userId"/> <!-- 主键映射 -->
        <result column="user_name" property="userName"/> <!-- 普通字段映射 -->
        <result column="user_age" property="userAge"/>
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
    </resultMap>

    <!-- 1. 基础查询:根据ID查用户(使用ResultMap) -->
    <select id="selectUser" parameterType="Integer" resultMap="UserResultMap">
        SELECT user_id, user_name, user_age, create_time 
        FROM users 
        WHERE user_id = #{id}  <!-- #{id}:预编译占位符,防SQL注入 -->
    </select>

    <!-- 2. 多参数查询:按姓名和年龄查用户(用@Param注解) -->
    <select id="selectUserByNameAndAge" resultMap="UserResultMap">
        SELECT * FROM users 
        WHERE user_name = #{name} AND user_age = #{age}
        <!-- 接口方法需加@Param:User selectUserByNameAndAge(@Param("name") String name, @Param("age") Integer age) -->
    </select>

    <!-- 3. 插入:新增用户并返回自增主键 -->
    <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="userId">
        <!-- useGeneratedKeys=true:开启自增主键返回;keyProperty:主键对应实体属性名 -->
        INSERT INTO users (user_name, user_age, create_time)
        VALUES (#{userName}, #{userAge}, #{createTime})
    </insert>
</mapper>
对应的 Mapper 接口(UserMapper.java

接口方法需与映射文件的 id、参数类型、返回值类型完全匹配

package com.zhanghe.study.mybatis.mapper;

import com.zhanghe.study.mybatis.model.User;
import org.apache.ibatis.annotations.Param;

public interface UserMapper {
    // 根据ID查用户(对应映射文件selectUser)
    User selectUser(Integer userId);
    
    // 多参数查询(需@Param注解绑定参数名)
    User selectUserByNameAndAge(@Param("name") String userName, @Param("age") Integer userAge);
    
    // 新增用户(返回自增主键注入到User的userId属性)
    int insertUser(User user);
}

执行流程:从配置加载到 SQL 执行

MyBatis 的执行流程可拆解为「配置加载→核心对象创建→SQL 执行→资源释放」四步,每一步都有明确的职责和生命周期管理要求。

核心流程拆解

(1)步骤 1:加载配置,创建 SqlSessionFactory

SqlSessionFactory 是 MyBatis 的 “工厂”,负责创建 SqlSession,其生命周期是全局唯一(应用启动时创建一次,避免重复创建导致连接泄漏)。

public static SqlSessionFactory createFactory() {
    try {
        // 1. 读取全局配置文件(Resources是MyBatis提供的工具类,简化classpath资源加载)
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        // 2. 通过SqlSessionFactoryBuilder构建SqlSessionFactory
        // (SqlSessionFactoryBuilder用完即丢,无需长期持有)
        return new SqlSessionFactoryBuilder().build(is);
    } catch (IOException e) {
        throw new RuntimeException("MyBatis配置文件加载失败!", e);
    }
}

关键原理
SqlSessionFactoryBuilder 会解析 mybatis-config.xml 和所有 Mapper.xml,将配置信息封装到 Configuration 对象中,最终基于 Configuration 创建 SqlSessionFactory(默认实现是 DefaultSqlSessionFactory)。

(2)步骤 2:创建 SqlSession

SqlSession 相当于 JDBC 的 Connection,代表一次数据库会话,生命周期是单次业务请求(线程不安全,需及时关闭)。

// 方式1:自动提交事务(openSession(true))
SqlSession session = sqlSessionFactory.openSession(true);

// 方式2:手动提交事务(默认,适合多SQL操作需事务一致性场景)
SqlSession session = sqlSessionFactory.openSession();
try {
    // 执行SQL
    session.commit(); // 手动提交
} catch (Exception e) {
    session.rollback(); // 异常回滚
} finally {
    session.close(); // 关闭会话
}

核心注意事项

  • SqlSession 线程不安全,不可跨线程共享;
  • 必须在 finally 块中关闭 SqlSession,避免数据库连接泄漏;
  • 手动提交模式下,未调用 commit() 会导致事务未提交,数据不生效。
(3)步骤 3:执行 SQL(新旧版本对比)

MyBatis 提供两种执行 SQL 的方式,新版本的「Mapper 接口代理」是主流(类型安全、可读性强)。

执行方式 代码示例 优缺点对比
老版本(SqlSession 直接调用) User user = session.selectOne("com.zhanghe.study.mybatis.mapper.UserMapper.selectUser", 2); 优点:无需定义接口; 缺点:字符串硬编码(易写错)、无类型校验、可读性差。
新版本(Mapper 接口代理) UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUser(2); 优点:类型安全(编译期校验)、代码简洁、可维护性强; 缺点:需定义接口。

原理补充
MyBatis 会为 Mapper 接口动态生成代理对象(基于 JDK 动态代理),调用接口方法时,代理对象会解析 namespace + methodName 找到对应的 SQL,执行后返回结果。

(4)步骤 4:资源释放

无论 SQL 执行成功或失败,都需关闭 SqlSession,释放数据库连接(归还到连接池):

SqlSession session = null;
try {
    session = sqlSessionFactory.openSession(true);
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectUser(2);
    System.out.println(user);
} finally {
    if (session != null) {
        session.close(); // 关闭SqlSession,释放连接
    }
}

工程化优化:工具类封装

为避免重复代码,推荐封装 MyBatisUtil 工具类,统一管理 SqlSessionFactorySqlSession

package com.zhanghe.study.mybatis.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtil {
    // 静态变量:确保SqlSessionFactory全局唯一
    private static SqlSessionFactory sqlSessionFactory;

    // 静态代码块:应用启动时初始化SqlSessionFactory
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream is = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            throw new ExceptionInInitializerError("MyBatis初始化失败!", e);
        }
    }

    // 获取SqlSession(自动提交事务,简化单SQL操作)
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession(true);
    }

    // 关闭SqlSession
    public static void closeSqlSession(SqlSession session) {
        if (session != null) {
            session.close();
        }
    }
}

工具类使用示例

// 业务代码中使用工具类
SqlSession session = null;
try {
    session = MyBatisUtil.getSqlSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectUser(2);
    System.out.println(user);
} finally {
    MyBatisUtil.closeSqlSession(session);
}

常见问题与解决方案

1. 映射文件找不到(Invalid bound statement (not found)

  • 原因 1mybatis-config.xml<mappers> 配置路径错误(如 resource 路径不是 classpath 下的路径);
  • 原因 2:Mapper 接口与映射文件不同包同名(如接口在 com.mapper,映射文件需在 resources/com/mapper 下且名为 UserMapper.xml);
  • 解决方案:使用 <package name="com.zhanghe.study.mybatis.mapper"/> 批量扫描 Mapper 接口,确保接口与映射文件同包同名。

2. 驼峰命名不生效(数据库字段 user_name → 实体属性 userNamenull

  • 原因:未开启全局设置 mapUnderscoreToCamelCase

  • 解决方案:在mybatis-config.xml的<settings>中添加:

    <setting name="mapUnderscoreToCamelCase" value="true"/>

3. SQL 注入风险(使用 ${} 占位符)

  • 原因${} 是字符串直接拼接(如 ${id} → 替换为参数值,无预编译),#{} 是预编译占位符(推荐);
  • 解决方案:除动态表名 / 排序字段(如 ORDER BY ${field})外,全部使用 #{}

4. SqlSession 未关闭导致连接泄漏

  • 原因:未在 finally 块中关闭 SqlSession,异常时资源未释放;
  • 解决方案:使用工具类的 closeSqlSession 方法,在 finally 中强制关闭。

总结

MyBatis 的基础使用核心是 “配置 + 接口 + 映射”:

  1. 配置文件:全局配置管理数据库和框架参数,映射文件定义 SQL 和实体映射;
  2. 执行流程SqlSessionFactory(全局唯一)→ SqlSession(单次会话)→ Mapper 代理(执行 SQL);
  3. 最佳实践:封装工具类管理核心对象、开启驼峰映射简化配置、使用接口代理避免硬编码

MyBatis 映射文件深度解析:从基础 CRUD 到复杂关联与动态 SQL

MyBatis 映射文件(通常命名为 XxxMapper.xml)是 SQL 语句与 Java 接口的 “桥梁”,承担着SQL 定义、参数映射、结果转换、缓存配置四大核心职责。相较于全局配置文件,映射文件更贴近业务逻辑,是 MyBatis 灵活可控的关键。本文在基础 CRUD 之上,补充动态 SQL、批量操作、复杂关联映射、缓存优化工程最佳实践,覆盖 90% 以上的开发场景。

核心 CRUD 操作:细节与扩展

MyBatis 通过 <select><insert><update><delete> 标签实现 CRUD,每个标签都有丰富的属性控制 SQL 执行逻辑,需重点关注事务提交、主键生成、批量操作三大核心场景。

基础 CRUD 与事务提交

MyBatis 的 SqlSession 默认不自动提交事务,需手动调用 commit() 或开启自动提交。

(1)基础示例
<!-- 1. 查询:根据 ID 获取用户 -->
<select id="getUserById" parameterType="Long" resultType="com.example.pojo.User">
    SELECT id, user_name AS userName, age FROM t_user WHERE id = #{id}
</select>

<!-- 2. 插入:新增用户 -->
<insert id="insertUser" parameterType="com.example.pojo.User">
    INSERT INTO t_user (user_name, age) VALUES (#{userName}, #{age})
</insert>

<!-- 3. 更新:修改用户信息 -->
<update id="updateUser" parameterType="com.example.pojo.User">
    UPDATE t_user SET user_name = #{userName}, age = #{age} WHERE id = #{id}
</update>

<!-- 4. 删除:根据 ID 删除用户 -->
<delete id="deleteUser" parameterType="Long">
    DELETE FROM t_user WHERE id = #{id}
</delete>
(2)事务提交方式
阅读全文 »

MyBatis 全局配置文件深度解析:从核心节点到工程实践

MyBatis 全局配置文件(默认命名为 mybatis-config.xml)是 MyBatis 框架的 “总开关”,用于配置数据库连接、映射规则、缓存策略、插件等核心参数。其配置节点有严格的顺序要求(必须按 properties→settings→typeAliases→typeHandlers→objectFactory→plugins→environments→databaseIdProvider→mappers 排列,乱序会导致解析报错),每个节点都承担特定职责。本文将逐一拆解每个节点的核心作用、配置方式、实际应用场景及工程最佳实践,帮助开发者掌握 MyBatis 配置的精髓。

根节点 <configuration>:配置入口

  • 核心作用:作为所有配置节点的父容器,定义 MyBatis 的全局配置上下文。

  • 关键规则:子节点必须按固定顺序排列(MyBatis 解析时会严格校验顺序,顺序错误会抛出 ConfigurationException)。

  • 示例结构:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!-- 子节点按固定顺序排列 -->
        <properties/>
        <settings/>
        <typeAliases/>
        <typeHandlers/>
        <objectFactory/>
        <plugins/>
        <environments/>
        <databaseIdProvider/>
        <mappers/>
    </configuration>

<properties>:属性配置(外部化配置)

核心作用

将数据库连接信息(如 URL、用户名、密码)、环境变量等外部化,避免硬编码在配置文件中,支持动态替换(如开发 / 测试 / 生产环境切换)。

三种配置方式与优先级

MyBatis 加载属性的优先级从低到高如下(高优先级覆盖低优先级):

阅读全文 »

MyBatis 深度解析:从核心特性到与 JDBC、Hibernate 的全方位对比

MyBatis 作为 Java 生态中半自动化持久层框架的代表,以 “SQL 与代码分离、灵活可控、轻量高效” 的特性,成为互联网项目(尤其是对 SQL 优化有强需求的场景)的首选。本文将从 MyBatis 的核心定义出发,深入剖析其对 JDBC 的封装优化、与 Hibernate 的本质差异,并补充工程实践中的关键配置与最佳实践,帮助开发者全面理解 MyBatis 的设计理念与适用场景。

MyBatis 核心概念与设计理念

什么是 MyBatis?

MyBatis 最初是 Apache 的开源项目 iBatis,2010 年更名为 MyBatis。它是一款半自动化持久层框架,核心是 “将 SQL 编写与 Java 业务代码分离”,同时封装 JDBC 的冗余操作(如连接管理、参数设置、结果映射),让开发者专注于 SQL 逻辑本身。

核心特性
  • 半自动化:区别于 Hibernate 的 “全自动 ORM”,MyBatis 仅自动完成 “参数映射→SQL 执行→结果映射”,SQL 需开发者手动编写(或通过注解配置),兼顾灵活性与效率;
  • SQL 与代码分离:SQL 集中配置在 XML 文件或通过注解定义,修改 SQL 无需改动 Java 代码,降低维护成本;
  • 强大的映射能力:支持复杂结果映射(如一对一、一对多关联)、自定义类型转换器,适配各种数据库表结构与 Java 对象的映射场景;
  • 轻量级:无复杂依赖,学习成本低,配置简单,可快速集成到 Spring、Spring Boot 等主流框架;
  • 支持动态 SQL:通过 <if><choose><foreach> 等标签编写动态 SQL,适配多条件查询、批量操作等场景。

设计理念:“专注 SQL,简化封装”

MyBatis 的设计围绕 “开发者对 SQL 拥有绝对控制权” 展开:

阅读全文 »
0%