乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 MyBatis概述与快速上手

    • 本章定位
    • MyBatis简介
    • 环境搭建
    • 第一个MyBatis程序
    • SqlSessionFactoryBuilder与openSession重载
    • SqlSessionFactory与SqlSession
    • SqlSession核心方法
    • 不使用 XML 构建 SqlSessionFactory
    • Mapper接口与映射方式
    • Java API 目录结构
  • 第2章 全局配置文件详解

    • 本章定位
    • properties
    • settings
    • typeAliases
    • typeHandlers
    • objectFactory
    • plugins
    • environments
    • transactionManager
    • dataSource
    • databaseIdProvider
    • mappers
    • 日志配置
  • 第3章 SQL映射文件基础

    • 本章定位
    • select
    • insert
    • update
    • delete
    • 参数传递与占位符
    • 主键生成策略
    • resultType
    • resultMap
    • 自动映射详解
    • sql片段
    • SQL 语句构建器
  • 第4章 动态SQL

    • 本章定位
    • if
    • choose、when、otherwise
    • where
    • set
    • foreach
    • trim
    • bind
    • script 元素:在注解映射器中启用动态 SQL
    • _databaseId 与动态 SQL 的多数据库支持
    • 动态 SQL 中插入脚本语言
  • 第5章 结果映射与关联查询

    • 本章定位
    • resultMap详解
    • association
    • collection
    • discriminator
    • N+1查询问题
    • 延迟加载
  • 第6章 MyBatis注解开发

    • 本章定位
    • @Select
    • @Insert
    • @Update
    • @Delete
    • @Param
    • @Options
    • @SelectKey
    • @Results
    • @Result
    • @One
    • @Many
    • @SelectProvider
  • 第7章 缓存与性能优化

    • 本章定位
    • 一级缓存
    • 二级缓存
    • 缓存配置详解
    • 自定义缓存
    • Executor执行器类型
    • 分页插件

settings

导学

本节学习目标:

  • 理解 settings 是 MyBatis 全局行为控制的"总控面板"
  • 掌握缓存、懒加载、驼峰映射、自动生成主键等核心开关的默认值与适用场景
  • 能够根据项目需求合理调整 settings,避免性能陷阱与行为偏差
  • 了解 settings 在 MyBatis 初始化阶段的解析与生效机制

定义

MyBatis 的默认行为并非适用于所有项目。例如:

  • 有些项目希望数据库下划线字段自动映射到 Java 驼峰属性,省去写 resultMap 的繁琐
  • 有些项目需要开启二级缓存提升读性能
  • 有些项目依赖 MySQL 自增主键,希望插入后自动回填 ID 到实体对象

settings 元素正是为了解决这些全局行为差异化需求而存在的。它包含数十个开关项,每一项都深刻影响 MyBatis 的运行时表现。


适用位置与核心属性

settings 位于 mybatis-config.xml 中 properties 之后、typeAliases 之前。

核心结构:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="false"/>
  <!-- 更多 setting... -->
</settings>
属性名默认值说明
name必填setting 的名称,必须是 MyBatis 支持的合法 key
value必填对应 setting 的取值,类型由具体 setting 决定(boolean/string/enum)

核心原理

MyBatis 初始化配置解析流程

关键源码逻辑:XMLConfigBuilder.settingsElement(Properties props) 方法会遍历所有 setting 标签,通过 Configuration 类中对应的 setter 方法将值注入。例如 cacheEnabled 对应 Configuration.setCacheEnabled(boolean)。这些值一旦在 SqlSessionFactory 创建时确定,后续整个应用生命周期内生效(除非重新构建 SqlSessionFactory)。


完整示例

场景说明

乐途公司员工管理系统使用 MySQL 5.7,表字段命名采用下划线风格(employee_name、create_time),而 Java 实体采用驼峰命名(employeeName、createTime)。同时系统需要:

  • 开启二级缓存减少数据库压力
  • 开启驼峰自动映射减少 resultMap 编写
  • 插入员工记录后自动获取 MySQL 自增主键
  • 开启懒加载优化关联部门信息的查询性能

操作前的状态

Employee 实体类:

public class Employee {
    private Integer employeeId;
    private String employeeName;
    private String departmentCode;
    private Date createTime;
    // getter / setter 省略
}

数据库表:

CREATE TABLE employee (
    employee_id INT PRIMARY KEY AUTO_INCREMENT,
    employee_name VARCHAR(50) NOT NULL,
    department_code VARCHAR(20),
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

完整配置代码

mybatis-config.xml:

<?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 resource="db.properties"/>

  <!-- 全局行为总控面板 -->
  <settings>
    <!-- 开启二级缓存,提升查询性能 -->
    <setting name="cacheEnabled" value="true"/>

    <!-- 开启懒加载,关联对象按需查询 -->
    <setting name="lazyLoadingEnabled" value="true"/>

    <!-- 关闭激进懒加载,避免触发不必要的关联查询 -->
    <setting name="aggressiveLazyLoading" value="false"/>

    <!-- 开启驼峰映射:employee_name -> employeeName -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>

    <!-- 插入后自动获取数据库生成的主键 -->
    <setting name="useGeneratedKeys" value="true"/>

    <!-- 指定默认执行器为 SIMPLE -->
    <setting name="defaultExecutorType" value="SIMPLE"/>

    <!-- 自动映射行为:对未明确映射的字段做部分自动映射 -->
    <setting name="autoMappingBehavior" value="PARTIAL"/>

    <!-- 指定日志实现为 SLF4J -->
    <setting name="logImpl" value="SLF4J"/>
  </settings>

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>

  <mappers>
    <mapper resource="mapper/EmployeeMapper.xml"/>
  </mappers>

</configuration>

EmployeeMapper.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.feixiang.mapper.EmployeeMapper">

  <!-- 由于 mapUnderscoreToCamelCase=true,无需 resultMap 即可自动映射 -->
  <select id="selectById" resultType="com.feixiang.entity.Employee">
    SELECT employee_id, employee_name, department_code, create_time
    FROM employee
    WHERE employee_id = #{id}
  </select>

  <!-- useGeneratedKeys=true 生效,插入后 employeeId 会被回填 -->
  <insert id="insert" useGeneratedKeys="true" keyProperty="employeeId">
    INSERT INTO employee (employee_name, department_code)
    VALUES (#{employeeName}, #{departmentCode})
  </insert>

</mapper>

Java 测试代码:

public class SettingsDemo {
    public static void main(String[] args) throws Exception {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

        try (SqlSession session = factory.openSession()) {
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

            // 测试插入并回填主键
            Employee emp = new Employee();
            emp.setEmployeeName("张三");
            emp.setDepartmentCode("DEV001");
            mapper.insert(emp);
            session.commit();

            System.out.println("插入后自动回填的主键: " + emp.getEmployeeId());

            // 测试驼峰自动映射查询
            Employee result = mapper.selectById(emp.getEmployeeId());
            System.out.println("查询结果: " + result.getEmployeeName() 
                + ", 创建时间: " + result.getCreateTime());
        }
    }
}

实际效果/结果

控制台输出:

插入后自动回填的主键: 1
查询结果: 张三, 创建时间: 2024-01-15 09:30:00

效果验证:

  • employee_id 自动映射到 employeeId,无需 resultMap
  • 插入后 employeeId 从 null 变为 1,说明 useGeneratedKeys 生效
  • 若关闭 mapUnderscoreToCamelCase,employeeName 和 createTime 将为 null

易错场景/常见误区

误区错误表现正解
认为 useGeneratedKeys=true 全局生效后所有插入都自动回填对非自增主键表(如 UUID 主键)插入后对象 ID 仍为 nulluseGeneratedKeys 仅对支持自增的数据库(MySQL、SQL Server)有效;Oracle 序列需用 selectKey
开启 lazyLoadingEnabled 但不关闭 aggressiveLazyLoading只要访问对象任意方法就触发所有懒加载属性查询,性能更差两者同时配置:lazyLoadingEnabled=true + aggressiveLazyLoading=false
mapUnderscoreToCamelCase=true 后仍写冗长 resultMap代码冗余,维护成本增加简单场景直接依赖自动映射,复杂场景(字段别名、类型转换)再用 resultMap
随意修改 defaultExecutorType 为 BATCH普通查询也走批处理执行器,导致查询结果异常或事务行为不符合预期BATCH 仅用于批量插入/更新场景,且应在 openSession(ExecutorType.BATCH) 时临时指定
cacheEnabled=true 但 Mapper XML 中未加 <cache/>二级缓存并未真正启用,只是开放了开关全局开关 cacheEnabled 是必要条件,还需在每个 Mapper 中声明 <cache/> 或 @CacheNamespace

面试考点

Q1:mapUnderscoreToCamelCase 开启后,MyBatis 是如何实现下划线到驼峰的转换的?

A:MyBatis 在 DefaultObjectWrapper 和 MetaObject 处理属性映射时,会调用 PropertyTokenizer 和 MapWrapper。开启该设置后,MyBatis 在查找目标属性时,会将下划线字段名转换为驼峰形式进行匹配,例如 employee_name 会尝试匹配 employeeName。这一转换发生在 ResultSetHandler 处理结果集的阶段。

Q2:lazyLoadingEnabled 和 aggressiveLazyLoading 有什么区别?

A:lazyLoadingEnabled 控制是否开启懒加载(即关联对象是否在首次访问时才查询)。aggressiveLazyLoading 控制懒加载的"激进程度":当为 true 时,只要调用对象的任意方法(包括 toString、equals、hashCode),就会触发所有懒加载属性;当为 false 时,只有真正访问该属性时才触发查询。生产环境推荐 true + false 的组合。

Q3:为什么 settings 一旦设置后,整个应用生命周期都生效?

A:因为 settings 的解析发生在 SqlSessionFactory 构建阶段,所有值被写入 Configuration 对象。SqlSessionFactory 是线程安全的单例,其内部的 Configuration 不会被修改。后续每次 openSession() 都复用同一份 Configuration,因此 settings 具有全局性和不可变性。

Q4:autoMappingBehavior 的三个取值 NONE、PARTIAL、FULL 有什么区别?

A:NONE 表示完全关闭自动映射,所有字段必须通过 resultMap 显式配置;PARTIAL(默认)表示对未嵌套的结果对象自动映射简单属性,但嵌套对象不自动映射;FULL 表示对所有结果对象(包括嵌套对象)都进行自动映射。推荐默认 PARTIAL,在简化代码的同时避免嵌套对象映射混乱。


小结

settings 是 MyBatis 全局配置中最具"影响力"的元素,它决定了缓存、懒加载、命名映射、主键生成、日志实现等核心行为。理解每个开关的默认值、生效时机和适用边界,是避免"配置一行,排查半天"的关键。建议团队在项目初期就统一 settings 模板,并在代码评审中作为必检项。


下一章引子

配置好全局行为后,Mapper XML 中频繁出现的全限定类名(如 com.feixiang.entity.Employee)显得冗长且易错。MyBatis 提供了 typeAliases 机制,允许为 Java 类型注册简短别名,让 XML 更加简洁可读。接下来,我们将学习别名的注册方式与内置别名清单。

上一页
properties
下一页
typeAliases