第1章:MyBatis 简介与环境搭建
1.1 什么是 MyBatis
| 概念名称 | 说明 | 注意事项 |
|---|
| ORM 框架 | 对象关系映射(Object Relational Mapping),将数据库记录映射为 Java 对象 | MyBatis 是半自动 ORM,需手动编写 SQL,但结果可自动映射到对象 |
| MyBatis 定义 | 一个支持普通 SQL 查询、存储过程和高级映射的持久层框架 | 不是全自动 ORM(如 Hibernate),更灵活,适合复杂 SQL 和性能要求高的场景 |
| 核心思想 | 将 SQL 语句从 Java 代码中分离,通过 XML 或注解配置 SQL 映射关系 | 提高 SQL 可维护性,便于优化和调试 |
| 轻量级 | 相比 Hibernate,MyBatis 更轻量,学习成本低,易于集成 | 适用于中小型项目或对 SQL 控制要求高的大型项目 |
1.2 MyBatis 的核心组件
| 组件名称 | 说明 | 注意事项 |
|---|
| SqlSessionFactoryBuilder | 用于构建 SqlSessionFactory,通过读取配置文件或 Java 配置创建实例 | 构建完成后即可丢弃,通常为局部变量 |
| SqlSessionFactory | 创建 SqlSession 的工厂,是线程安全的,应作为单例存在 | 应用中通常只创建一个实例,建议使用静态常量或 Spring 管理 |
| SqlSession | 执行 SQL 命令的接口,包含增删改查方法,非线程安全 | 每个线程应有独立的 SqlSession 实例,使用后必须关闭 |
| Mapper 接口 | 数据访问接口,方法对应 SQL 操作,由 MyBatis 动态代理实现 | 接口方法名需与 Mapper XML 中的 id 一致,参数和返回值需匹配 |
| Mapper XML 文件 | 定义 SQL 语句及映射规则的 XML 文件,与 Mapper 接口绑定 | namespace 必须等于接口全限定名,statementId 等于方法名 |
| Configuration | 包含 MyBatis 所有配置信息的对象,由 mybatis-config.xml 解析生成 | 在 SqlSessionFactory 初始化时创建,贯穿整个应用生命周期 |
1.3 搭建 MyBatis 开发环境
| 步骤 | 说明 | 注意事项 |
|---|
| 引入依赖(Maven) | 添加 mybatis 和数据库驱动依赖 | 确保版本兼容,例如 MySQL 驱动为 mysql-connector-java |
| 创建数据库表 | 准备测试用的数据表(如 user 表) | 表结构清晰,字段命名符合规范 |
| 编写实体类 | 创建与数据库表对应的 Java Bean 类 | 属性名与字段名一致或通过 resultMap 映射 |
| 创建 Mapper 接口 | 定义数据操作方法(如 selectUserById) | 方法签名需与 SQL 映射匹配 |
| 编写 Mapper XML | 在 resources 目录下创建 XML 文件,定义 SQL 语句 | 文件路径与接口包结构一致,namespace 正确 |
| 配置 mybatis-config.xml | 配置 environments、mappers 等信息 | environment 指定事务管理器和数据源,mappers 注册映射文件 |
| 测试运行 | 使用 SqlSessionFactory 构建 SqlSession 并调用 Mapper 方法 | 记得 commit() 写操作,finally 中 close() SqlSession |
1.4 第一个 MyBatis 程序(Hello World)
| 方法/步骤 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| Resources.getResourceAsStream | Resources.getResourceAsStream("mybatis-config.xml") | 加载 MyBatis 配置文件流 | InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); | 配置文件需在 classpath 下(如 resources 目录) |
| SqlSessionFactoryBuilder.build | new SqlSessionFactoryBuilder().build(inputStream) | 从配置流构建 SqlSessionFactory | SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); | Builder 为临时对象,构建后可回收 |
| SqlSessionFactory.openSession | factory.openSession() | 获取 SqlSession 实例 | SqlSession session = factory.openSession(); | 查询可不传参,增删改建议使用 openSession(true) 自动提交 |
| SqlSession.getMapper | session.getMapper(UserMapper.class) | 获取 Mapper 接口的代理实现 | UserMapper mapper = session.getMapper(UserMapper.class); | 接口必须已注册到 configuration 中 |
| Mapper 接口方法调用 | mapper.selectUserById(1) | 执行 SQL 查询 | User user = mapper.selectUserById(1); | 方法名、参数类型、返回类型必须与 XML 中定义一致 |
| SqlSession.close | session.close() | 关闭 SqlSession,释放资源 | try { ... } finally { session.close(); } | 必须关闭,否则可能导致连接泄露 |
第2章:MyBatis 核心配置文件详解
2.1 mybatis-config.xml 文件结构
| 元素名称 | 说明 | 注意事项 |
|---|
<configuration> | 根元素,包含所有其他配置节点 | 所有子元素必须按固定顺序排列 |
<properties> | 外部属性配置,用于引入外部 .properties 文件或定义键值对 | 可用于替换配置中的 ${key} 占位符,提高可维护性 |
<settings> | 全局行为配置,控制 MyBatis 运行时行为 | 如开启缓存、延迟加载、日志等,影响全局 |
<typeAliases> | 类型别名,为 Java 类设置简短名字 | 减少 XML 中全限定类名的重复书写,如为 com.example.User 设 alias=“User” |
<typeHandlers> | 类型处理器,处理 Java 类型与 JDBC 类型之间的转换 | 可自定义处理特殊类型(如枚举、JSON) |
<objectFactory> | 对象工厂,创建结果对象的实例 | 一般使用默认即可,可自定义对象创建逻辑 |
<plugins> | 插件配置,用于拦截 Executor、StatementHandler 等核心对象 | 实现拦截器扩展功能,如分页、日志等 |
<environments> | 环境配置,可配置多个 environment,指定事务管理和数据源 | 每个 environment 有唯一 id,通过 default 指定默认环境 |
<databaseIdProvider> | 数据库厂商标识提供者,根据数据库类型执行不同 SQL | 支持多数据库兼容,如 Oracle、MySQL 使用不同方言 |
<mappers> | 注册 Mapper 映射文件或接口 | 必须注册才能被 MyBatis 扫描到,支持 class、resource、url、package 方式 |
⚠️ 注意:<configuration> 子元素必须按上述顺序声明,否则解析报错。
2.2 核心配置项
| 配置项名称 | 语法格式 | 用途 | 示例值 | 注意事项 |
|---|
| environment id | <environment id="development"> | 定义环境唯一标识 | development, test, production | 必须与 <environments default="..."> 中的值匹配 |
| transactionManager type | <transactionManager type="JDBC"/> | 指定事务管理器类型 | JDBC, MANAGED | JDBC 使用 Connection.commit()/rollback(),MANAGED 交由容器管理 |
| dataSource type | <dataSource type="POOLED"> | 指定数据源类型 | UNPOOLED, POOLED, JNDI | POOLED 使用连接池,UNPOOLED 每次新建连接,JNDI 用于 Web 容器环境 |
| mappers resource | <mapper resource="UserMapper.xml"/> | 注册基于 classpath 的 Mapper XML 文件 | 路径以 / 分隔,如 com/example/UserMapper.xml | 文件必须存在于 classpath,且 namespace 正确 |
| mappers class | <mapper class="com.example.UserMapper"/> | 注册 Mapper 接口 | 接口全限定名 | 接口需与 XML 同名同包,或使用注解方式 |
| mappers package | <package name="com.example.mapper"/> | 批量注册指定包下的所有 Mapper 接口 | 包名 | 推荐方式,减少重复配置 |
2.3 数据源(DataSource)配置
| 数据源类型 | 说明 | 配置示例 | 注意事项 |
|---|
| UNPOOLED | 每次请求都打开新连接,使用后立即关闭 | <dataSource type="UNPOOLED"> | 性能差,仅用于测试或简单场景 |
| POOLED | 使用连接池管理数据库连接,复用连接提升性能 | <dataSource type="POOLED"> | 推荐生产环境使用,内置简单连接池,默认最大连接数 10 |
| JNDI | 从 Web 容器(如 Tomcat)的 JNDI 查找数据源 | <dataSource type="JNDI"> | 适用于 Web 应用部署在应用服务器中 |
| 属性名 | 说明 | 常用值 | 注意事项 |
|---|
| driver | JDBC 驱动类名 | com.mysql.cj.jdbc.Driver | 根据数据库选择正确驱动 |
| url | 数据库连接 URL | jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC | 注意字符集、时区、SSL 等参数 |
| username | 数据库用户名 | root | 建议使用专用账号 |
| password | 数据库密码 | 123456 | 生产环境应加密存储 |
| poolMaximumActiveConnections | 最大活跃连接数 | 10 | 默认 10,可根据负载调整 |
| poolMaximumIdleConnections | 最大空闲连接数 | 5 | 避免连接浪费 |
2.4 事务管理器(TransactionManager)配置
| 事务管理器类型 | 说明 | 配置方式 | 注意事项 |
|---|
| JDBC | 使用底层 JDBC 的事务机制,通过 Connection 的 commit/rollback 控制 | <transactionManager type="JDBC"/> | 适用于简单应用或与 Spring 未集成时 |
| MANAGED | 将事务管理委托给容器(如 JBoss、Tomcat),MyBatis 不主动提交或回滚 | <transactionManager type="MANAGED"/> | 通常与 Spring 集成时使用,Spring 会覆盖此设置 |
| 属性名 | 说明 | 示例值 | 注意事项 |
|---|
| closeConnection | 是否由 MyBatis 关闭连接(仅 MANAGED 有效) | false | 若容器管理连接,设为 false,避免容器报错 |
2.5 设置(settings)详解
| 设置项名称 | 说明 | 可选值 | 默认值 | 注意事项 |
|---|
| cacheEnabled | 是否启用全局二级缓存 | true / false | true | 关闭后所有 Mapper 二级缓存失效 |
| lazyLoadingEnabled | 是否启用延迟加载 | true / false | false | 需配合 aggressiveLazyLoading 使用 |
| aggressiveLazyLoading | 是否积极加载,true 则访问任一属性触发加载 | true / false | true | 设为 false 可避免无谓加载 |
| multipleResultSetsEnabled | 是否允许单个语句返回多个结果集 | true / false | true | 通常无需修改 |
| useColumnLabel | 是否优先使用列标签(alias)而非列名 | true / false | true | 推荐开启,SQL 中使用 AS 定义别名 |
| useGeneratedKeys | 是否使用 JDBC getGeneratedKeys() 获取自增主键 | true / false | false | 插入记录后获取主键值需开启 |
| autoMappingBehavior | 自动映射级别(NONE, PARTIAL, FULL) | NONE, PARTIAL, FULL | PARTIAL | PARTIAL 不映射嵌套结果,FULL 映射全部 |
| defaultExecutorType | 默认执行器类型 | SIMPLE, REUSE, BATCH | SIMPLE | BATCH 用于批量操作,提升性能 |
| defaultStatementTimeout | SQL 执行超时时间(秒) | 整数值 | null | 防止慢查询阻塞 |
| mapUnderscoreToCamelCase | 是否开启下划线转驼峰命名 | true / false | false | 数据库字段 user_name → Java 属性 userName |
| logImpl | 指定日志实现类 | SLF4J, LOG4J, LOG4J2, JDK_LOGGING 等 | 未设置 | 推荐使用 SLF4J + Logback |
提示:这些设置可通过 Java 代码动态配置,但通常在 XML 中统一管理。
第3章:Mapper 映射文件与 SQL 执行
3.1 Mapper 接口与 XML 映射文件
| 概念名称 | 说明 | 注意事项 |
|---|
| Mapper 接口 | 定义数据访问方法的 Java 接口,由 MyBatis 动态代理实现 | 方法名必须与 XML 中的 id 一致,参数和返回类型需匹配 |
| XML 映射文件 | 包含 SQL 语句和映射规则的 XML 文件,与 Mapper 接口绑定 | 文件路径应与接口包结构一致(如 com/example/UserMapper.xml) |
| namespace | XML 文件根元素 <mapper> 的 namespace 属性 | 必须等于对应 Mapper 接口的全限定类名(如 com.example.mapper.UserMapper) |
| statementId | <select>, <insert> 等元素的 id 属性 | 必须与接口中方法名完全一致 |
| 绑定机制 | MyBatis 通过 namespace + id 定位 SQL 语句 | 若未正确绑定,调用接口方法时会抛出 BindingException |
| 注解 vs XML | 可使用注解(如 @Select)替代 XML,但复杂 SQL 建议用 XML | 混合使用时,XML 优先级高于注解 |
3.2 select 元素详解
| 属性名称 | 语法格式 | 用途 | 示例值 | 注意事项 |
|---|
| id | id="selectUserById" | 唯一标识该 SQL 语句,对应接口方法名 | selectUserById | 必须唯一,且与接口方法名一致 |
| parameterType | parameterType="int" | 指定输入参数类型(可选,MyBatis 可自动推断) | int, com.example.User, map | 基本类型可写别名(如 int, string),复杂类型写全限定名或别名 |
| resultType | resultType="com.example.User" | 指定返回值类型,MyBatis 自动映射列到属性 | User, int, java.util.Map | 若字段名非驼峰,建议开启 mapUnderscoreToCamelCase |
| resultMap | resultMap="userResultMap" | 引用外部 <resultMap> 定义,用于复杂映射 | userResultMap | 优先级高于 resultType,适用于关联查询、嵌套对象等 |
| timeout | timeout="10" | 设置 SQL 执行超时时间(秒) | 10 | 超时后抛出异常,防止长时间阻塞 |
| fetchSize | fetchSize="100" | 提示 JDBC 驱动每次从数据库获取的行数 | 100 | 用于优化大结果集查询性能 |
| statementType | statementType="PREPARED" | 指定使用 Statement 类型 | STATEMENT, PREPARED, CALLABLE | 推荐 PREPARED(预编译),防止 SQL 注入 |
3.3 insert 元素详解
| 属性名称 | 语法格式 | 用途 | 示例值 | 注意事项 |
|---|
| id | id="insertUser" | 唯一标识插入语句 | insertUser | 必须与接口方法名一致 |
| parameterType | parameterType="User" | 指定传入参数类型 | User, map | 支持对象、Map、基本类型 |
| keyProperty | keyProperty="id" | 指定将自增主键值回填到对象的哪个属性 | id | 用于获取数据库生成的主键(如 MySQL AUTO_INCREMENT) |
| keyColumn | keyColumn="user_id" | 指定数据库主键列名(多列主键时使用) | user_id | 单列主键可省略 |
| useGeneratedKeys | useGeneratedKeys="true" | 是否使用 JDBC getGeneratedKeys() 获取主键 | true / false | 需数据库支持,且配合 keyProperty 使用 |
| flushCache | flushCache="true" | 是否清空本地缓存(一级缓存) | true / false | 插入操作默认为 true |
3.4 update 元素详解
| 属性名称 | 语法格式 | 用途 | 示例值 | 注意事项 |
|---|
| id | id="updateUser" | 唯一标识更新语句 | updateUser | 必须与接口方法名一致 |
| parameterType | parameterType="User" | 指定传入参数类型 | User, map | 支持对象、Map、基本类型 |
| timeout | timeout="5" | 设置执行超时时间(秒) | 5 | 防止长时间阻塞 |
| flushCache | flushCache="true" | 是否清空本地缓存 | true / false | 更新操作默认为 true |
| statementType | statementType="PREPARED" | 使用预编译语句 | PREPARED | 推荐使用,防止 SQL 注入 |
3.5 delete 元素详解
| 属性名称 | 语法格式 | 用途 | 示例值 | 注意事项 |
|---|
| id | id="deleteUserById" | 唯一标识删除语句 | deleteUserById | 必须与接口方法名一致 |
| parameterType | parameterType="int" | 指定传入参数类型 | int, string | 支持基本类型、对象、Map |
| flushCache | flushCache="true" | 是否清空本地缓存 | true / false | 删除操作默认为 true |
| timeout | timeout="5" | 设置执行超时时间 | 5 | 防止慢查询阻塞 |
3.6 参数传递(parameterType)
| 参数类型 | 语法格式 | 用途 | 代码示例 | 注意事项 |
|---|
| 单个基本类型 | #{id}, ${value} | 传递单个参数(int, String 等) | select * from user where id = #{id} | 可使用任意名称(如 #{id}),MyBatis 自动识别 |
| 单个 POJO 对象 | #{property} | 传递 Java Bean 对象,通过属性访问 | insert into user(name, age) values(#{name}, #{age}) | 属性名对应对象字段 |
| Map 类型 | #{key} | 传递 Map,key 为参数名 | Map<String, Object> param = new HashMap<>(); param.put("name", "Tom"); | 灵活,适合动态参数 |
| 多个参数 | #{param1}, #{param2} 或 @Param 注解 | 传递多个参数 | select * from user where name=#{name} and age=#{age} | 建议使用 @Param(“name”) 指定名称,避免 param1、param2 不直观 |
| Collection / List | #{collection}, #{list}, #{array} | 用于 <foreach> 遍历 | foreach collection="list" item="item" | MyBatis 自动封装为 Map,key 为 collection/list/array |
3.7 结果映射(resultType 与 resultMap)
| 映射方式 | 说明 | 代码示例 | 注意事项 |
|---|
| resultType | 简单类型自动映射,MyBatis 根据列名自动匹配属性 | <select id="..." resultType="User"> | 要求列名与属性名一致或开启 mapUnderscoreToCamelCase |
| resultMap | 自定义结果映射,支持复杂关系(一对一、一对多) | <resultMap id="userMap" type="User">...</resultMap> | 适用于嵌套对象、字段名不一致、关联查询等场景 |
<id> 子元素 | 映射主键列,提升性能 | <id property="id" column="user_id"/> | 建议显式定义主键映射 |
<result> 子元素 | 映射普通列 | <result property="name" column="user_name"/> | 用于字段名与属性名不一致的情况 |
| autoMapping | 控制单个 resultMap 是否启用自动映射 | <resultMap ... autoMapping="true"> | 可与手动映射共存 |
第4章:动态 SQL
4.1 if 标签
| 属性名称 | 语法格式 | 用途 | 代码示例 | 注意事项 |
|---|
| test | test="condition" | 判断条件是否成立,成立则包含其内容 | <if test="name != null">AND name = #{name}</if> | 常用于 WHERE 条件拼接,注意避免多余的 AND/OR |
4.2 choose when otherwise 标签
| 标签名称 | 语法格式 | 用途 | 代码示例 | 注意事项 |
|---|
<choose> | <choose>...</choose> | 类似 Java 中的 switch-case,选择一个分支执行 | <choose><when test="...">...</when><otherwise>...</otherwise></choose> | 只执行第一个满足条件的 <when> |
<when> | <when test="condition">SQL</when> | 判断条件,成立则执行 | <when test="type == 1">AND status = 'ACTIVE'</when> | test 属性为 OGNL 表达式 |
<otherwise> | <otherwise>SQL</otherwise> | 默认分支,当所有 <when> 不满足时执行 | <otherwise>AND status = 'INACTIVE'</otherwise> | 相当于 default |
4.3 where 标签
| 说明 | 代码示例 | 注意事项 |
|---|
| 自动处理 WHERE 关键字,并移除开头的 AND 或 OR | <where><if test="name != null">AND name = #{name}</if></where> | 避免手动判断条件是否存在,防止 SQL 语法错误 |
4.4 set 标签
| 说明 | 代码示例 | 注意事项 |
|---|
| 用于 UPDATE 语句,自动添加 SET 关键字,并移除末尾多余的逗号 | <set><if test="name != null">name = #{name},</if></set> | 解决动态更新字段时的逗号问题 |
4.5 trim 标签
| 属性名称 | 语法格式 | 用途 | 代码示例 | 注意事项 |
|---|
| prefix | prefix="WHERE" | 添加前缀 | <trim prefix="WHERE" prefixOverrides="AND"> | 常用于自定义 WHERE 或 SET 逻辑 |
| prefixOverrides | prefixOverrides="AND" | 移除指定的前缀内容 | 移除开头的 AND 或 OR | 多个用竖线分隔(如 `AND |
| suffix | suffix=";" | 添加后缀 | suffix=")" | 如添加括号或分号 |
| suffixOverrides | suffixOverrides="," | 移除指定的后缀内容 | 移除末尾的逗号 | 常用于 <set> 替代 |
4.6 foreach 集合标签
| 属性名称 | 语法格式 | 用途 | 代码示例 | 注意事项 |
|---|
| collection | collection="list" | 指定要遍历的集合(List, Set, Array, Map) | collection="ids" | 若参数为单个 List,直接用 “list”;多个参数需用 @Param |
| item | item="item" | 当前元素的变量名 | #{item} | 在循环体内使用 |
| open | open="(" | 循环开始前添加的内容 | open="(" | 常用于 IN 查询 |
| close | close=")" | 循环结束后添加的内容 | close=")" | 匹配 open |
| separator | separator="," | 元素之间的分隔符 | separator="," | 避免末尾多出分隔符 |
4.7 bind 属性
| 属性名称 | 语法格式 | 用途 | 代码示例 | 注意事项 |
|---|
| name | name="usernameLike" | 定义变量名 | name="pattern" | 在当前 SQL 片段中可用 |
| value | value="'%' + username + '%'" | OGNL 表达式计算值 | value="'%" + _parameter + "%'" | 常用于模糊查询,避免在 Java 层拼接字符串 |
示例:<bind name="likeName" value="'%' + name + '%'"/> AND name LIKE #{likeName}
第5章:结果映射与关联查询
5.1 resultMap 基础配置
| 元素/属性 | 语法格式 | 用途 | 示例值 | 注意事项 |
|---|
<resultMap> | <resultMap id="..." type="...">...</resultMap> | 定义一个结果映射规则 | id="userResultMap" type="User" | type 可为别名或全限定类名 |
<id> | <id property="id" column="user_id"/> | 映射主键字段,提升性能和唯一性识别 | property 对应 Java 属性,column 对应数据库列 | 建议显式定义,尤其在关联查询中 |
<result> | <result property="name" column="user_name"/> | 映射普通字段 | 支持基本类型、String 等 | 用于字段名与属性名不一致的情况 |
| extends | extends="baseResultMap" | 继承另一个 resultMap,实现复用 | baseResultMap | 避免重复定义公共字段 |
| autoMapping | autoMapping="true" | 控制该 resultMap 是否启用自动映射(可覆盖全局设置) | true / false | 优先级高于全局 autoMapping 设置 |
✅ 使用场景:字段名下划线 vs 属性名驼峰、主键非 id、需要继承复用等。
5.2 association 关联映射(一对一)
| 属性/元素 | 说明 | 代码示例 | 注意事项 |
|---|
| property | Java 对象中的关联属性名 | property="dept" | 必须与 POJO 中的字段名一致 |
| javaType | 关联对象的 Java 类型 | javaType="Department" | 可省略(MyBatis 自动推断),复杂时建议指定 |
| resultType / resultMap | 指定返回类型或引用外部映射 | resultMap="deptResultMap" | 推荐使用 resultMap 实现复杂嵌套 |
| 嵌套查询(select) | 通过另一个 SQL 查询加载关联对象 | <association property="dept" column="dept_id" select="findDeptById"/> | 需配合 column 传递参数,易产生 N+1 查询问题 |
| 嵌套结果(内联) | 使用 JOIN 一次性查出所有数据 | <association property="dept" javaType="Dept"><id .../><result .../></association> | 推荐方式,避免多次数据库访问 |
⚠️ N+1 问题:若主查询返回 N 条记录,每条都触发一次关联查询,则共执行 N+1 次 SQL。
5.3 collection 集合映射(一对多)
| 属性/元素 | 说明 | 代码示例 | 注意事项 |
|---|
| property | 集合属性名(如 List<User> users) | property="users" | 必须是集合类型(List, Set, Array) |
| ofType / javaType | 集合中元素的类型(MyBatis 3.5+ 推荐 ofType) | ofType="User" | 替代旧版的 javaType=“ArrayList” |
| resultType / resultMap | 映射集合元素 | resultMap="userResultMap" | 复杂对象建议使用 resultMap |
| 嵌套查询(select) | 通过独立 SQL 查询加载集合 | <collection property="orders" column="user_id" select="findOrders"/> | 存在 N+1 问题,可通过 fetchType=“lazy” 延迟加载优化 |
| 嵌套结果(JOIN) | 使用 LEFT JOIN 一次性查出主从数据 | 内联 <id>, <result> 定义映射规则 | 推荐用于一对多,注意避免笛卡尔积 |
✅ 最佳实践:优先使用 JOIN + 嵌套结果,性能更优。
5.4 嵌套查询与延迟加载
| 概念 | 说明 | 配置方式 | 注意事项 |
|---|
| 嵌套查询(Nested Select) | 利用 select 属性调用另一个 mapped statement 加载关联数据 | 在 <association> 或 <collection> 中使用 select="xxx" | 简单但可能导致 N+1 查询 |
| 延迟加载(Lazy Loading) | 关联对象在真正被访问时才加载,提升初始查询性能 | 全局配置:<setting name="lazyLoadingEnabled" value="true"/> | 必须同时开启 aggressiveLazyLoading=false(默认) |
| fetchType | 控制单个关联是否立即加载或延迟加载 | fetchType="lazy" 或 "eager" | 可覆盖全局设置,灵活控制 |
| 代理机制 | MyBatis 使用 CGLIB 或 JAVASSIST 创建代理对象 | 无需手动干预,自动实现 | 注意:需将关联对象设为 protected,避免序列化问题 |
📌 配置示例:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
5.5 自动映射(autoMapping)
| 级别 | 配置方式 | 行为说明 | 注意事项 |
|---|
| 全局级别 | <setting name="autoMappingBehavior" value="PARTIAL"/> | 控制整个 SqlSessionFactory 的自动映射行为 | 可选值:NONE, PARTIAL, FULL |
| resultMap 级别 | <resultMap id="..." type="..." autoMapping="true"> | 覆盖全局设置,仅对该 resultMap 生效 | 更加灵活,推荐使用 |
| 映射规则 | 数据库列名 → Java 属性名 | 默认按”下划线转驼峰”匹配(如 user_name → userName) | 需开启 mapUnderscoreToCamelCase=true |
| 级别 | PARTIAL | FULL |
|---|
| PARTIAL | 忽略带 <association> / <collection> 的自动映射 | 推荐 PARTIAL,避免复杂映射出错 |
| FULL | 全部自动映射 | 可能导致意外映射 |
✅ 适用场景:简单 POJO、字段命名规范、快速开发原型。
第6章:缓存机制
6.1 一级缓存(SqlSession 级别)
| 特性 | 说明 | 生命周期 | 注意事项 |
|---|
| 作用范围 | 同一个 SqlSession 内 | 从创建到关闭 | 不同 SqlSession 之间不共享 |
| 缓存机制 | 基于 PerpetualCache 的 HashMap 实现 | 缓存 key:MappedStatement ID + 查询参数 | 执行 commit()、close() 或 DML 操作会清空缓存 |
| 自动启用 | 默认开启,无需配置 | 查询语句自动缓存结果 | 同一 SqlSession 中相同查询只查一次数据库 |
| 清空时机 | insert/update/delete 操作后、手动 clearCache()、SqlSession 关闭 | 保证数据一致性 | 若跨 SqlSession 更新数据,可能读到旧值 |
✅ 验证方式:同一 SqlSession 中执行两次相同 select,第二次无 SQL 输出。
6.2 二级缓存(Mapper 级别)
| 特性 | 说明 | 配置方式 | 注意事项 |
|---|
| 作用范围 | 同一个 namespace(即同一个 Mapper 接口) | 所有 SqlSession 共享 | 需手动启用 |
| 启用方式 | 在 Mapper XML 中添加 <cache/> | 或使用注解 @CacheNamespace | 必须实现 Serializable 接口 |
| 缓存策略 | 默认 LRU(Least Recently Used) | 可配置 eviction=“LRU/FIFO/SOFT/WEAK” | 结合 size、flushInterval 等属性控制 |
| flushInterval | 缓存刷新间隔(毫秒) | flushInterval="60000" | 定时清空,防止内存溢出 |
| readOnly | 是否只读(影响序列化) | readOnly=“true” → 返回相同实例;false → 可序列化副本 | false 更安全,避免并发修改 |
🔐 安全性:涉及敏感数据的 Mapper 应禁用二级缓存。
6.3 缓存配置与刷新策略
| 配置项 | 语法格式 | 用途 | 示例值 |
|---|
| size | size="1024" | 最多缓存对象数 | 1024 |
| flushInterval | flushInterval="60000" | 每隔多少毫秒清空一次缓存 | 60000(1分钟) |
| eviction | eviction="LRU" | 回收策略:LRU(最近最少)、FIFO(先进先出)、SOFT(软引用)、WEAK(弱引用) | LRU |
| blocking | blocking="true" | 若缓存未命中,是否阻塞其他线程等待加载 | true / false |
| 刷新策略 | 触发方式 | 说明 |
|---|
| DML 操作 | 执行 insert/update/delete 后自动清空该 namespace 缓存 | 保证数据一致性 |
| 手动清空 | sqlSession.getMapper(UserMapper.class).clearCache() | 强制刷新缓存 |
| 定时刷新 | 配置 flushInterval | 适用于变化较少的数据 |
6.4 自定义缓存(Cache 接口)
| 步骤 | 说明 | 示例 |
|---|
| 实现 Cache 接口 | 自定义缓存逻辑(如 Redis、Ehcache) | public class RedisCache implements Cache { ... } |
| 使用装饰器模式 | 可包装现有缓存增强功能(如同步、日志) | 继承 Decorator 类 |
| 配置方式 | 在 <cache> 中指定 type | <cache type="com.example.cache.RedisCache"/> |
| 序列化支持 | 缓存对象必须实现 Serializable | 否则抛出异常 |
| 第三方集成 | 支持与 Redis、Memcached 等集成 | 需引入对应客户端依赖 |
💡 典型场景:分布式系统中使用 Redis 作为二级缓存,提升并发性能。
第7章:插件与拦截器
7.1 MyBatis 插件机制原理
| 核心概念 | 说明 |
|---|
| 责任链模式 | MyBatis 将核心组件(Executor, StatementHandler, ParameterHandler, ResultSetHandler)包装在代理中,形成拦截链。 |
| 代理机制 | 基于 JDK 动态代理或 CGLIB 实现,在目标方法执行前后插入自定义逻辑。 |
| 签名(@Signature) | 定义插件要拦截的类、方法和参数类型,是插件注册的关键元数据。 |
| 插件注册 | 通过 <plugins> 配置或 SqlSessionFactoryBuilder 注册,MyBatis 自动织入拦截逻辑。 |
✅ 拦截点生命周期:
Executor → StatementHandler → ParameterHandler → ResultSetHandler
7.2 实现 Interceptor 接口
| 方法 | 用途 | 参数说明 |
|---|
Object intercept(Invocation invocation) | 核心逻辑:在此实现拦截处理 | invocation: 包含被调用对象、方法、参数 |
Object plugin(Object target) | 返回被代理的对象(通常用 Plugin.wrap(target, this)) | target: 被拦截的接口实例 |
void setProperties(Properties properties) | 接收插件配置属性 | 可从 XML 中传入参数,如分页大小 |
📌 代码模板:
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class ExampleInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 执行前逻辑
Object result = invocation.proceed(); // 放行执行
// 执行后逻辑
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 处理配置属性
}
}
7.3 拦截 Executor、StatementHandler 等
| 组件 | 拦截用途 | 典型应用场景 |
|---|
| Executor | 控制 SQL 执行流程 | 分页、缓存控制、性能监控、事务管理增强 |
| StatementHandler | 操作 SQL 语句与参数 | SQL 改写(如加租户字段)、慢 SQL 记录 |
| ParameterHandler | 参数设置过程 | 敏感字段加密、默认值填充 |
| ResultSetHandler | 结果集处理 | 字段脱敏、自动补全、结果转换 |
⚠️ 注意事项:
- 拦截 Executor 影响范围最大,慎用。
- 修改 SQL 时注意保留原有逻辑(如
#{} ${} 解析)。
- 避免在拦截器中进行耗时操作,影响性能。
7.4 实现分页插件示例
| 步骤 | 说明 | 示例代码/配置 |
|---|
| 1. 定义注解或参数 | 标识需要分页的查询 | 使用 RowBounds 或自定义 Page<T> 对象 |
| 2. 拦截 Executor.query() | 在查询前修改 SQL 添加 LIMIT / ROWNUM | 判断是否存在 RowBounds 非空 |
| 3. 获取总记录数 | 执行 SELECT COUNT(*) 查询 | 基于原 SQL 构造统计语句 |
| 4. 改写原始 SQL | 添加分页子句(MySQL: LIMIT ?,?;Oracle: ROWNUM) | 使用 BoundSql 获取原始 SQL |
| 5. 封装分页结果 | 将数据 + 总数封装为 PageInfo | 返回 PageResult<T> 对象 |
💡 简化版逻辑:
public Object intercept(Invocation invocation) {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
RowBounds rowBounds = (RowBounds) args[2];
if (rowBounds != RowBounds.DEFAULT) {
// 1. 获取总数
int total = countTotal(ms, args[1]);
// 2. 改写 SQL 加入 LIMIT
String sql = rewriteSqlWithLimit(ms, rowBounds);
// 3. 设置新 BoundSql
...
// 4. 返回包含总数的结果
return new PageResult<>(resultList, total);
}
return invocation.proceed();
}
✅ 推荐方案:使用成熟框架如 PageHelper。
第8章:注解开发模式
8.1 使用注解替代 XML(@Select, @Insert 等)
| 注解 | 用途 | 示例 |
|---|
@Select("SELECT * FROM user WHERE id = #{id}") | 查询 | 替代 <select> |
@Insert("INSERT INTO user(name) VALUES(#{name})") | 插入 | 替代 <insert> |
@Update("UPDATE user SET name=#{name} WHERE id=#{id}") | 更新 | 替代 <update> |
@Delete("DELETE FROM user WHERE id=#{id}") | 删除 | 替代 <delete> |
@Options(useGeneratedKeys=true, keyProperty="id") | 控制主键生成 | 类似 <insert> 的属性 |
@Results + @Result | 映射复杂结果 | 替代 <resultMap> |
✅ 优点:简洁、无需维护 XML 文件
❌ 缺点:复杂 SQL 可读性差,难以调试
8.2 注解中的动态 SQL(@SelectProvider 等)
| 注解 | 用途 | 示例 |
|---|
@SelectProvider(type = SqlProvider.class, method = "buildSelectById") | 动态生成 SELECT 语句 | 提供类 + 方法名 |
@InsertProvider | 动态 INSERT | 构建条件化插入语句 |
@UpdateProvider | 动态 UPDATE | 如根据字段是否为空更新 |
@DeleteProvider | 动态 DELETE | 条件删除 |
📌 SqlProvider 示例:
public class UserSqlProvider {
public String buildSelectById(Long id) {
return new SQL() {{
SELECT("*");
FROM("user");
if (id != null) {
WHERE("id = #{id}");
}
}}.toString();
}
}
✅ 优势:结合 Java 逻辑生成 SQL,灵活性高
🔧 工具类支持:org.apache.ibatis.jdbc.SQL 辅助构建
8.3 注解与 XML 混合使用
| 场景 | 配置方式 | 注意事项 |
|---|
| 同一个 Mapper 中混合 | 可同时存在注解方法和 XML 定义 | MyBatis 会合并解析 |
| 优先级 | XML 定义覆盖注解 | 若同名方法存在于 XML,则忽略注解 |
| 引用 resultMap | 注解中可用 @ResultMap("xxx") 引用 XML 中定义的 <resultMap> | 实现复用 |
| 事务与缓存 | 缓存需在 XML 中配置 <cache/> | 注解方式无法直接启用二级缓存 |
| 最佳实践 | 简单 CRUD 用注解;复杂查询、关联映射用 XML | 提升可维护性 |
✅ 配置示例:
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
@ResultMap("userResultMap") // 引用 XML 中定义的 resultMap
User findById(Long id);
List<User> findAll(); // 对应 XML 中的 <select id="findAll">...
}
💡 建议:团队统一风格,避免混乱;大型项目推荐以 XML 为主。
第9章:Spring 集成
9.1 MyBatis 与 Spring 整合原理
| 核心机制 | 说明 |
|---|
| 依赖注入 | Spring 容器管理 MyBatis 的核心对象(SqlSessionFactory、SqlSessionTemplate、Mapper 接口)。 |
| 声明式事务 | 基于 DataSourceTransactionManager 实现,通过 @Transactional 注解控制事务边界。 |
| 代理模式 | Spring 使用 JDK 动态代理为 Mapper 接口生成实现类,内部委托给 SqlSessionTemplate 执行 SQL。 |
| 生命周期管理 | SqlSession 由 Spring 自动创建、使用、关闭,避免资源泄漏。 |
| 整合包 | 使用 mybatis-spring 桥接 MyBatis 与 Spring,提供 SqlSessionFactoryBean 和 MapperScannerConfigurer 等组件。 |
✅ 优势:简化配置、统一事务管理、无缝集成 Spring 生态。
9.2 配置 SqlSessionFactoryBean
| 属性 | 用途 | 示例值 |
|---|
| dataSource | 指定数据源(必须) | ref=“dataSource” |
| configLocation | MyBatis 全局配置文件路径(可选) | classpath:mybatis-config.xml |
| mapperLocations | 指定 Mapper XML 文件位置 | classpath:mapper/*.xml |
| typeAliasesPackage | 扫描别名包,自动注册类型别名 | com.example.entity |
| plugins | 注册拦截器(如分页插件) | <array><ref bean="pageInterceptor"/></array> |
📌 XML 配置示例:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<property name="typeAliasesPackage" value="com.example.entity"/>
<property name="plugins">
<array>
<ref bean="pageHelper"/>
</array>
</property>
</bean>
✅ Java Config 示例:
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/*.xml"));
factoryBean.setTypeAliasesPackage("com.example.entity");
return factoryBean.getObject();
}
| 属性 | 用途 | 示例值 |
|---|
| basePackage | 指定 Mapper 接口所在的包 | com.example.mapper |
| sqlSessionFactoryBeanName | 关联 SqlSessionFactory | sqlSessionFactory |
| annotationClass | 只扫描带有指定注解的接口(可选) | org.apache.ibatis.annotations.Mapper |
| markerInterface | 只扫描继承指定接口的 Mapper(可选) | BaseMapper.class |
📌 XML 配置示例:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
✅ 效果:Spring 自动扫描包下所有 Mapper 接口,并注册为 Bean,无需手动实现。
9.4 使用 @Mapper 注解
| 特性 | 说明 |
|---|
| 作用 | 标记接口为 MyBatis Mapper,配合 @MapperScan 使用 |
| 位置 | 直接加在 Mapper 接口上 |
| 配合注解 | @MapperScan("com.example.mapper") 在配置类上启用扫描 |
| 与 MapperScannerConfigurer 的关系 | 二者功能等价,@MapperScan 是注解方式的替代 |
📌 Java Config 示例:
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
// 配置 SqlSessionFactory...
}
@Mapper
public interface UserMapper {
User findById(Long id);
}
✅ 建议:推荐使用 @MapperScan 批量扫描,避免每个接口都加 @Mapper。
第10章:Spring Boot 集成
10.1 引入 mybatis-spring-boot-starter
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
| 特性 | 说明 |
|---|
| 自动配置 | Spring Boot 自动配置 SqlSessionFactory、SqlSessionTemplate、DataSource |
| 简化配置 | 无需手动配置 SqlSessionFactoryBean 和 MapperScannerConfigurer |
| 版本兼容 | 注意与 Spring Boot 版本匹配(如 3.0.x 支持 Spring Boot 3) |
✅ 启动类无需额外注解:只要 Mapper 在组件扫描路径下即可。
10.2 配置文件(application.yml)设置
| 配置项 | 说明 | 示例 |
|---|
mybatis.mapper-locations | 指定 XML 映射文件路径 | classpath:mapper/*.xml |
mybatis.type-aliases-package | 类型别名包 | com.example.entity |
mybatis.configuration.map-underscore-to-camel-case | 开启下划线转驼峰 | true |
mybatis.configuration.lazy-loading-enabled | 启用延迟加载 | true |
mybatis.configuration.default-statement-timeout | 默认超时时间(秒) | 30 |
📌 application.yml 示例:
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
lazy-loading-enabled: true
aggressive-lazy-loading: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印 SQL
10.3 注解方式开发
| 步骤 | 说明 |
|---|
| 1. 创建 Mapper 接口 | 使用 @Select, @Insert 等注解编写 SQL |
| 2. 添加 @Mapper | 或使用 @MapperScan 扫描 |
| 3. 注入使用 | 在 Service 中 @Autowired 注入 Mapper |
📌 示例:
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User findById(@Param("id") Long id);
@Insert("INSERT INTO user(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
}
✅ 适合场景:简单 CRUD、SQL 固定、快速开发。
10.4 XML 方式开发
| 步骤 | 说明 |
|---|
| 1. 配置 mapper-locations | 确保 XML 文件路径正确 |
| 2. 编写 Mapper 接口 | 只定义方法,不加注解 |
| 3. 编写 XML 文件 | namespace 对应接口全限定名,id 对应方法名 |
| 4. 使用 @MapperScan | 启用 Mapper 扫描 |
📌 XML 示例(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">
<mapper namespace="com.example.mapper.UserMapper">
<select id="findById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
✅ 优势:支持复杂 SQL、动态 SQL、结果映射,易于维护和调试。
10.5 集成分页插件(如 PageHelper)
1. 添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.7</version>
</dependency>
2. 配置(可选):application.yml 中配置数据库类型、合理化分页等
3. 使用方式:在查询前调用 PageHelper.startPage(pageNum, pageSize)
📌 yml 配置示例:
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
📌 代码使用示例:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public PageInfo<User> getPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.findAll(); // 紧跟查询
return new PageInfo<>(users);
}
}
✅ 特点:自动改写 SQL 实现分页,支持多种数据库,返回 PageInfo 包含总数、页码等信息。
第11章:最佳实践与性能优化
11.1 SQL 语句优化建议
| 优化策略 | 说明 | 实践建议 |
|---|
避免 SELECT * | 减少网络传输和内存消耗 | 明确列出所需字段,提升可读性与性能 |
| 使用索引字段查询 | 加速数据检索 | WHERE、JOIN、ORDER BY中的字段应建立合适索引 |
| 避免 N+1 查询问题 | 防止因关联查询导致大量数据库访问 | 使用JOIN + 嵌套结果映射,或启用延迟加载 |
| 合理使用分页 | 控制返回数据量 | 使用LIMIT/ROWNUM分页,避免全表加载 |
| 减少子查询嵌套 | 子查询可能影响执行计划 | 尽量用JOIN替代深层嵌套子查询 |
| 避免在 WHERE 中对字段进行函数操作 | 导致索引失效 | 如 WHERE YEAR(create_time) = 2023 → 改为范围查询 create_time BETWEEN '2023-01-01' AND '2023-12-31' |
使用预编译参数(#{}) | 防止 SQL 注入,提升执行效率 | 禁用 ${} 拼接用户输入,仅用于动态表名等可信场景 |
✅ 工具辅助:使用 EXPLAIN 分析执行计划,识别慢查询。
11.2 使用 resultMap 减少冗余
| 实践方式 | 说明 | 示例 |
|---|
| 定义基础 resultMap | 抽取公共字段(如 id, create_time) | <resultMap id="baseResultMap" type="User"><id property="id" column="id"/><result property="createTime" column="create_time"/></resultMap> |
| 继承复用(extends) | 避免重复定义 | <resultMap id="userWithDept" type="User" extends="baseResultMap"><association property="dept" resultMap="deptResultMap"/></resultMap> |
| 引用已有映射 | 多个语句共用同一映射规则 | <select id="findUserById" resultMap="userWithDept">...</select> 和 <select id="findUserList" resultMap="userWithDept">...</select> |
| 自动映射 + 手动补充 | 简单字段自动映射,复杂字段手动指定 | <resultMap id="autoWithAssoc" type="User" autoMapping="true"><association property="dept" column="dept_id" select="findDept"/></resultMap> |
✅ 优势:提高可维护性,降低出错率,减少代码重复。
11.3 合理使用缓存
| 缓存类型 | 使用建议 | 注意事项 |
|---|
| 一级缓存(SqlSession 级) | 默认开启,适用于单次请求内的重复查询 | 跨 SqlSession 不共享;DML 操作后自动清空 |
| 二级缓存(Mapper 级) | 适合读多写少、数据变化不频繁的场景(如字典表) | 必须实现 Serializable;集群环境下需配合 Redis 等分布式缓存 |
| 禁用缓存场景 | 实时性要求高的数据、频繁更新的表、敏感信息(如密码) | 设置 useCache="false" |
| 刷新策略 | 配置 flushInterval 或通过 DML 自动刷新 | 避免脏读 |
| 缓存粒度 | 按业务模块启用,避免全量缓存 | 使用 <cache> 配置 size、eviction 控制内存占用 |
✅ 最佳实践:
- 开启二级缓存前评估数据一致性要求。
- 使用
@CacheNamespace(readOnly = false) 支持序列化副本,避免并发修改问题。
11.4 批量操作(BatchExecutor)
| 场景 | 配置方式 | 性能对比 |
|---|
| 批量插入/更新/删除 | 使用 SqlSessionTemplate 或 SqlSession 的 ExecutorType.BATCH | 相比默认 SIMPLE 执行器,性能提升显著 |
| Spring 集成配置 | @Autowired private SqlSessionFactory sqlSessionFactory; 配合 openSession(ExecutorType.BATCH) | 1000 条插入:BATCH 比 SIMPLE 快 5~10 倍(取决于数据库) |
| MyBatis-Plus 等框架支持 | 提供 saveBatch() 方法封装批量逻辑 | 简化开发 |
| JDBC 层优化 | 启用 rewriteBatchedStatements=true(MySQL) | 进一步提升性能 |
public void batchInsert(List<User> users) {
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
users.forEach(mapper::insert);
session.commit();
}
}
⚠️ 注意事项:
- 批量操作不触发一级缓存。
- 异常处理需谨慎,部分成功时考虑事务回滚策略。
- 大数据量建议分页提交(如每 500 条 commit 一次),防止 OOM。
11.5 日志配置与调试
| 日志类型 | 配置方式 | 用途 |
|---|
| MyBatis SQL 日志 | 在 mybatis.configuration.log-impl 中设置 | 查看实际执行的 SQL 和参数 |
| 支持的日志实现 | StdOutImpl:控制台输出;Log4jImpl / Log4j2Impl;SLF4JImpl | 推荐使用 SLF4J + Logback 统一日志框架 |
| 结合 logback-spring.xml | <logger name="com.example.mapper" level="DEBUG"/> 和 <logger name="org.apache.ibatis" level="INFO"/> | 按 Mapper 包控制日志级别 |
| 显示参数值 | 使用 #{} 参数绑定自动打印 | ${} 不安全且不记录参数 |
| 格式化输出 | 配合 p6spy 或 mybatis-plus 的性能分析插件 | 格式化 SQL、统计执行时间 |
📌 application.yml 示例:
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
✅ 调试技巧:
- 生产环境关闭 SQL 日志,防止敏感信息泄露。
- 使用 @Flush 注解强制刷新批量操作。
- 结合 APM 工具(如 SkyWalking)监控慢查询。
💡 总结:性能优化核心原则
- SQL 优先:优化查询语句和索引是根本。
- 减少交互次数:避免 N+1,合理使用 JOIN 和批量操作。
- 缓存适度:根据业务特性启用缓存,警惕数据一致性。
- 日志可控:开发期充分调试,生产期合理降级。