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

    • 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执行器类型
    • 分页插件

自动映射详解

概述

自动映射(Auto-mapping)是 MyBatis 查询结果自动填充到 Java 对象字段的机制。你不需要为每个字段手动写 <result> 标签,MyBatis 会根据列名与属性名的匹配规则自动完成赋值。这个特性让简单的 CRUD 操作极其简洁,但也可能在不经意间引入隐蔽的 Bug。

飞翔科技的白歌在 Code Review 时就抓到过一起自动映射导致的线上数据泄露事故——那是后话。

三种自动映射模式

MyBatis 的 autoMappingBehavior 有三种模式,在全局 settings 中配置:

<settings>
    <setting name="autoMappingBehavior" value="PARTIAL"/>
</settings>

模式对比表

模式值行为典型场景
NONE禁用完全关闭自动映射,所有字段必须显式用 <result> 声明对安全性要求极高的金融系统、严格 DTO 映射
PARTIAL默认只对没有定义嵌套结果映射(association/collection)的 resultMap 进行自动映射绝大多数生产项目(MyBatis 推荐)
FULL全开对所有字段自动映射,包括嵌套结果映射内部快速原型、内部工具(需承担风险)

自动映射决策流程

飞翔科技实战场景

场景一:白歌的正常开发(PARTIAL 模式 — 推荐)

白歌查询员工信息,resultMap 只定义了主键映射,其余字段依赖自动映射:

<!-- EmployeeMapper.xml -->
<resultMap id="baseMap" type="com.feixiang.model.Employee">
    <id property="id" column="emp_id"/>
    <!-- name, email, dept_name 自动映射 -->
</resultMap>

<select id="findById" resultMap="baseMap">
    SELECT emp_id, name, email, dept_name FROM t_employee WHERE emp_id = #{id}
</select>

自动映射结果:

数据库列名Java 属性名匹配方式是否映射
emp_idid显式 <id>✅
namename自动(大小写不敏感)✅
emailemail自动✅
dept_namedeptName自动(下划线转驼峰,需开启 mapUnderscoreToCamelCase)✅(开启后)

场景二:孔蓝踩坑 — PARTIAL 模式下的嵌套陷阱

孔蓝查询员工及其所属部门,定义了嵌套结果映射:

<resultMap id="employeeWithDept" type="com.feixiang.model.Employee">
    <id property="id" column="emp_id"/>
    <result property="name" column="emp_name"/>
    <!-- 嵌套映射:每个员工关联一个部门 -->
    <association property="department" javaType="com.feixiang.model.Department">
        <id property="id" column="dept_id"/>
        <result property="name" column="dept_name"/>
    </association>
</resultMap>

<select id="findAllWithDept" resultMap="employeeWithDept">
    SELECT e.emp_id, e.emp_name, e.email,
           d.dept_id, d.dept_name
    FROM t_employee e
    LEFT JOIN t_department d ON e.dept_id = d.dept_id
</select>

问题:PARTIAL 模式下,外层 employeeWithDept 因为包含了 <association>,自动映射被禁用。孔蓝忘记声明 email 字段映射,导致查询结果中 email 始终为 null。

// 查询结果
Employee emp = mapper.findAllWithDept().get(0);
System.out.println(emp.getName());   // ✅ "孔蓝"
System.out.println(emp.getEmail());  // ❌ null(忘了声明映射!)
System.out.println(emp.getDepartment().getName()); // ✅ "研发部"

修复:显式声明被遗漏的字段。

<resultMap id="employeeWithDept" type="com.feixiang.model.Employee">
    <id property="id" column="emp_id"/>
    <result property="name" column="emp_name"/>
    <result property="email" column="email"/>  <!-- ← 补上! -->
    <association property="department" javaType="com.feixiang.model.Department">
        <id property="id" column="dept_id"/>
        <result property="name" column="dept_name"/>
    </association>
</resultMap>

场景三:大翔的安全审查 — 故意关闭自动映射

飞翔科技有一个薪资模块,涉及敏感数据。大翔要求必须显式声明所有字段映射,防止数据库新增敏感列被自动暴露:

<!-- 全局设置:不关,保持 PARTIAL -->
<!-- 但薪资相关的 resultMap 单独关闭自动映射 -->

<resultMap id="salaryMap" type="com.feixiang.model.Salary" autoMapping="false">
    <id property="id" column="salary_id"/>
    <result property="employeeId" column="emp_id"/>
    <result property="baseAmount" column="base_amount"/>
    <!-- 故意不声明 bonus_amount、stock_options 等敏感列 -->
    <!-- 即使 SQL 查了这些列也不会自动映射到对象上 -->
</resultMap>
// 即使 SQL 中写了 SELECT * FROM t_salary(不推荐!),
// 自动映射关闭后,未声明的字段不会被赋值,杜绝数据泄露。

场景四:FULL 模式的风险演示

假设项目全局设置了 FULL:

<settings>
    <setting name="autoMappingBehavior" value="FULL"/>
</settings>

黄俪在测试环境发现一个诡异现象:

<resultMap id="empMap" type="com.feixiang.model.Employee">
    <id property="id" column="emp_id"/>
    <association property="department" javaType="com.feixiang.model.Department">
        <!-- 内部没有显式映射,但 FULL 模式会强制自动映射 -->
    </association>
</resultMap>

FULL 模式下,嵌套的 <association> 内部也会自动映射。如果 SQL 返回了 dept_id 列,它会尝试映射到 Department.id。这看起来方便,但问题是:你可能根本不知道哪些列被映射了,数据库加一个列就可能意外改变对象状态。

autoMapping 属性覆盖全局设置

每个 <resultMap> 元素上可以用 autoMapping 属性覆盖全局设置:

<!-- 覆盖为开启 -->
<resultMap id="forceOnMap" type="User" autoMapping="true">
    <!-- 即使全局是 NONE,这里也启用 -->
</resultMap>

<!-- 覆盖为关闭 -->
<resultMap id="forceOffMap" type="User" autoMapping="false">
    <!-- 即使全局是 FULL,这里也关闭 -->
</resultMap>

自动映射与 resultType 的关系

resultType 是自动映射的最佳搭档:

<!-- resultType 不定义 resultMap,完全依赖自动映射 -->
<select id="findAll" resultType="com.feixiang.model.Employee">
    SELECT emp_id AS id, name, email FROM t_employee
</select>
特性resultTyperesultMap
自动映射始终启用(除非全局 NONE)受 autoMapping 属性控制
列名匹配依赖别名或下划线转驼峰可显式指定列-属性对应
适用场景简单查询、列名与属性名一致复杂映射、嵌套、构造器注入
灵活性低(无法精细控制)高(可逐个字段控制)

关键认知:resultType 本质上是 MyBatis 内部自动生成的一个匿名 resultMap,其 autoMapping 行为等同于该匿名 resultMap 未设置 autoMapping 属性时的行为——即继承全局设置。全局为 NONE 时,resultType 也不会自动映射。

易错场景提醒

1. 嵌套映射中漏声明字段

最隐蔽的坑:PARTIAL 模式 + association/collection → 自动映射静默关闭。
排查技巧:如果发现某字段始终为 null,检查 resultMap 是否包含嵌套映射。

2. 大小写和下划线转换

MyBatis 默认不开启下划线转驼峰,需手动设置:
<setting name="mapUnderscoreToCamelCase" value="true"/>
否则 dept_name 不会映射到 deptName,字段值直接丢失。

3. FULL 模式下数据库加列导致数据泄露

DBA 在 t_employee 表加了 salary 列,应用层没更新 resultMap,
但 FULL 模式自动把 salary 映射到了 Employee 对象上,
前端序列化 JSON 时把薪资数据发给所有用户——严重事故!

4. columnPrefix 与自动映射的微妙互动

<resultMap id="empMap" type="Employee">
    <association property="dept" columnPrefix="d_"
                 resultMap="deptMap"/>
</resultMap>
<!-- 如果 deptMap 开着自动映射且 dept 表有 30 个列,
     你实际上映射了 30 个你不知道的字段 -->

面试考点

  1. MyBatis 自动映射有哪三种模式?默认是哪种?

    • NONE(禁用)、PARTIAL(默认,嵌套映射中禁用)、FULL(全开包括嵌套)。
  2. PARTIAL 模式下什么情况会触发自动映射关闭?

    • resultMap 中包含 <association> 或 <collection> 嵌套映射时,该 resultMap 的自动映射会被禁用。
  3. 如何在单个 resultMap 上覆盖全局自动映射设置?

    • 在 <resultMap> 元素上使用 autoMapping="true|false" 属性。
  4. resultType 和自动映射是什么关系?

    • resultType 依赖自动映射完成列-属性匹配;它等价于一个没有显式 autoMapping 属性的匿名 resultMap,继承全局设置。
  5. 什么场景下应该关闭自动映射?

    • 处理敏感数据(薪资、密码)、需要严格 DTO 映射、DTO 字段远少于数据库列时(防止意外映射)。
上一页
resultMap
下一页
sql片段