MyBatis 的自动映射是怎么回事?为什么数据库 user_name 能映射到 Java 的 userName?
简化版
**MyBatis 的「自动映射」是指:查询结果的列,不用你在 resultMap 里一条条手写映射,MyBatis 会「自动」把「列名」和「Java 对象的属性名」对应起来。**默认规则是「列名和属性名相同就自动映射」(不区分大小写)——列 name 自动映射到属性 name。为什么 user_name 能映射到 userName:因为开启了「驼峰命名映射」mapUnderscoreToCamelCase=true(Spring Boot 默认开)——它让 MyBatis 把下划线命名的列(user_name)自动转成驼峰属性(userName),这正好匹配「数据库习惯下划线、Java 习惯驼峰」的通用规范。自动映射的三个级别(autoMappingBehavior):NONE(关闭自动映射)、PARTIAL(默认,自动映射没在 resultMap 里显式配的列,但不处理嵌套)、FULL(连嵌套结果也自动映射,有风险)。核心:自动映射省去了简单场景手写 resultMap 的麻烦;配合驼峰映射,user_name → userName 无缝对应;复杂映射(关联、嵌套)还是要手写 resultMap。
详细版
两个关键配置:
| 配置 | 值 | 作用 |
|---|---|---|
mapUnderscoreToCamelCase | true/false | 下划线列名↔驼峰属性(如 user_name↔userName) |
autoMappingBehavior | NONE/PARTIAL/FULL | 自动映射的级别 |
autoMappingBehavior 三个级别:
| 级别 | 含义 |
|---|---|
| NONE | 完全关闭自动映射(所有列必须在 resultMap 显式配) |
| PARTIAL | 默认。自动映射普通列,但嵌套 resultMap 不自动映射 |
| FULL | 连嵌套结果映射的列也自动映射(可能误映射,有风险) |
# Spring Boot 配置(application.yml)
mybatis:
configuration:
map-underscore-to-camel-case: true # 开启驼峰映射(Spring Boot 默认就是 true)
auto-mapping-behavior: partial # 自动映射级别
// 数据库表:user(id, user_name, create_time)
public class User {
private Long id; // ← 列 id
private String userName; // ← 列 user_name(驼峰映射自动对应)
private LocalDateTime createTime; // ← 列 create_time
}
// resultType 直接用(无需 resultMap),靠自动映射 + 驼峰映射对应
@Select("SELECT id, user_name, create_time FROM user WHERE id = #{id}")
User selectById(Long id);
⚠️ 自动映射依赖「列名」和「属性名」能对上,所以 SQL 里的别名很关键。如果查询的列名和属性对不上(比如复杂 SQL、函数列、多表 join 有重名列),自动映射就失效或映射错——这时要么用
AS起别名让列名匹配属性(SELECT u.name AS userName),要么写resultMap显式映射。特别是开启驼峰映射后,别名也要遵循规则:想映射到userName,列名(或别名)应该是user_name(下划线)或userName(直接驼峰)。另外,autoMappingBehavior=FULL虽然能自动映射嵌套结果,但风险是「不同表的同名列被错误映射到一起」,所以默认是PARTIAL(不碰嵌套),生产一般不建议开 FULL。
完整版教学
一、自动映射是什么
先理解「自动映射」省了什么事:
不用自动映射(手写 resultMap):
<resultMap id="userMap" type="User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
...每一列都手写一遍映射...
</resultMap>
→ 列多了很繁琐
用自动映射(resultType):
<select resultType="User">SELECT id, name, age FROM user</select>
→ MyBatis 自动把 列名 对应到 同名属性
→ 不用手写每一列的映射
自动映射的规则(默认):
查出来的每一列,找 Java 对象里"同名"的属性(不区分大小写)
列 name → 属性 name(有 setName 就设进去)
找不到同名属性的列 → 忽略(不报错)
所以用 resultType(而非 resultMap)时,就在用自动映射
自动映射是「MyBatis 自动把列名对应到同名属性,不用手写 resultMap 每一列」——用 resultType 时就在用它(默认规则:每列找同名属性,不区分大小写,找不到就忽略)。它省去了简单场景逐列手写 resultMap 的繁琐。理解「自动映射=MyBatis 自动把列名对应同名属性、用 resultType 就在用它、找不到同名属性就忽略、省去手写 resultMap」,就理解了自动映射的基本概念。
二、驼峰映射:下划线↔驼峰
user_name → userName 靠的是「驼峰命名映射」mapUnderscoreToCamelCase:
命名习惯的冲突:
数据库列名:习惯用下划线(snake_case)—— user_name、create_time
Java 属性名:习惯用驼峰(camelCase)—— userName、createTime
→ 直接自动映射对不上(user_name ≠ userName)
mapUnderscoreToCamelCase=true 的作用:
映射时,把下划线列名"转成驼峰"再匹配属性
user_name → userName(去掉下划线、后面字母大写)
create_time → createTime
→ 于是下划线的列 能映射到 驼峰的属性
Spring Boot 默认:
mybatis-spring-boot-starter 默认开启了驼峰映射
(原生 MyBatis 默认是 false,要手动开)
好处:
遵循两边的命名规范(DB 下划线、Java 驼峰)
又能自动映射,不用手写 resultMap 处理命名差异
user_name → userName 靠 mapUnderscoreToCamelCase=true(驼峰命名映射)——它解决「数据库习惯下划线(user_name)、Java 习惯驼峰(userName)」的命名冲突:映射时把下划线列名转成驼峰再匹配属性。Spring Boot 默认开启(原生 MyBatis 默认 false 要手动开)。好处是「两边各遵循自己的命名规范,又能自动映射」。理解「驼峰映射 mapUnderscoreToCamelCase 把下划线列名转驼峰匹配属性、解决 DB 下划线 vs Java 驼峰冲突、Spring Boot 默认开启」,就理解了 user_name→userName 的原理。
三、autoMappingBehavior 三级别
自动映射有三个级别,控制「自动到什么程度」:
autoMappingBehavior 三个值:
① NONE:完全关闭自动映射
所有列都必须在 resultMap 里显式配置,否则不映射
→ 最严格,一般不用(太麻烦)
② PARTIAL(默认):部分自动映射
- 对"没有嵌套 resultMap"的普通结果:自动映射
- 对"有嵌套 resultMap(association/collection)"的:
嵌套部分不自动映射(要显式配)
→ 平衡了便利和安全,是默认
③ FULL:完全自动映射
连嵌套结果映射里的列也自动映射
→ 风险:多表 join 时,不同表的同名列可能被错误地
自动映射到嵌套对象里(张冠李戴)
→ 一般不建议开
为什么默认 PARTIAL 不碰嵌套:
嵌套映射(一对一、一对多)涉及多表、多对象
自动映射容易把列映射错(同名列冲突)
→ 嵌套部分交给你显式配(resultMap 的 association/collection),更安全
autoMappingBehavior 有三级别:NONE(完全关闭,所有列必须显式配)、PARTIAL(默认,自动映射普通列但嵌套部分不自动)、FULL(连嵌套也自动映射,有风险)。默认 PARTIAL 不碰嵌套,是因为嵌套映射(一对一/一对多)涉及多表多对象,自动映射容易把同名列映射错(张冠李戴),所以嵌套交给你显式配更安全。FULL 风险大一般不开。理解「autoMappingBehavior:NONE(全关)/PARTIAL(默认,普通列自动嵌套不自动)/FULL(连嵌套也自动有风险)、默认不碰嵌套因多表同名列易映射错」,就掌握了自动映射的级别控制。
四、自动映射的匹配细节
深入理解自动映射「怎么匹配」,避免踩坑:
匹配过程(PARTIAL 默认,开启驼峰映射):
对结果集的每一列:
1. 拿列名(如 user_name)
2. 开了驼峰映射 → 转成驼峰(userName)
3. 在目标类型里找名为 userName 的属性(有 setUserName)
4. 找到 → 通过 TypeHandler 转类型、set 进去
5. 找不到 → 忽略这一列(不报错)
不区分大小写:
列名匹配属性时不区分大小写(NAME 也能匹配 name)
要注意的坑:
① 列名和属性对不上 → 该列不映射(属性是 null,静默)
解决:用 AS 别名让列名匹配 / 写 resultMap
② 函数列、表达式列没别名 → 列名是表达式,对不上属性
解决:SELECT COUNT(*) AS total → 匹配 total 属性
③ 多表 join 有重名列 → 可能映射混乱
解决:起别名区分 / 用 resultMap
★ 记住:自动映射靠"列名 ↔ 属性名"对应
对不上就映射不上(且默认静默不报错)→ 排查 null 值先看列名和属性名
自动映射的匹配过程:拿列名 → (开驼峰映射则)转驼峰 → 找同名属性(不区分大小写)→ 找到就 TypeHandler 转类型 set 进去、找不到就忽略(不报错)。要注意的坑:列名和属性对不上该列不映射(属性是 null,静默)、函数/表达式列要起别名、多表 join 重名列可能混乱——解决靠 AS 别名或写 resultMap。排查「属性是 null」时先看列名和属性名对不对得上。理解「自动映射匹配:列名→(驼峰)→找同名属性→转类型 set/找不到忽略、坑是列名对不上就静默 null、排查 null 先看列名属性对应」,就掌握了匹配细节和排查方法。
五、自动映射 vs resultMap:怎么选
自动映射不是万能的,要知道「什么时候用自动映射、什么时候写 resultMap」:
用自动映射(resultType):
✓ 单表查询、列名和属性能对上(或靠驼峰映射对上)
✓ 简单场景,省去手写 resultMap
例:SELECT id, user_name, age FROM user → resultType="User"
用 resultMap(显式映射):
✓ 列名和属性差异大、无法靠命名规则对上
✓ 需要关联映射(association 一对一、collection 一对多)
✓ 需要特殊 TypeHandler、鉴别器 discriminator
✓ 复杂多表 join
例:一个订单带用户信息和多个订单项 → resultMap + association + collection
混合:
resultMap 里也能开自动映射(autoMapping="true")
→ 显式配一部分(关联、特殊列),其余靠自动映射
→ 两者结合,减少工作量
选择原则:
简单单表 → resultType(自动映射)
关联/嵌套/复杂 → resultMap(显式)
选择原则:简单单表、列名能对上(或靠驼峰映射)→ 用 resultType(自动映射)省事;列名差异大、需要关联映射(association/collection)、特殊 TypeHandler、复杂多表 join → 写 resultMap(显式映射)。两者可混合(resultMap 里开 autoMapping="true",显式配一部分、其余自动)。理解「简单单表用 resultType 自动映射、关联/嵌套/复杂用 resultMap 显式、可混合(resultMap 开 autoMapping)」,就掌握了自动映射和 resultMap 的选择。
六、常见配置与实践
总结自动映射相关的配置和最佳实践:
Spring Boot 配置:
mybatis:
configuration:
map-underscore-to-camel-case: true # 驼峰映射(默认开)
auto-mapping-behavior: partial # 自动映射级别(默认 partial)
原生 MyBatis(mybatis-config.xml):
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
</settings>
最佳实践:
① 开启驼峰映射(DB 下划线、Java 驼峰,各遵规范)
② 简单查询用 resultType 靠自动映射
③ 复杂查询、关联映射用 resultMap
④ 保持 autoMappingBehavior=PARTIAL(别用 FULL,避免嵌套误映射)
⑤ 列名对不上时用 AS 别名,而不是硬开 FULL
排查"某字段查出来是 null":
先检查:列名(或别名)能否匹配属性名(考虑驼峰映射规则)
→ 大多是列名/属性名对不上导致自动映射失败
自动映射的最佳实践:开启驼峰映射(DB 下划线、Java 驼峰各遵规范)、简单查询用 resultType、复杂/关联用 resultMap、保持 autoMappingBehavior=PARTIAL(别用 FULL 避免嵌套误映射)、列名对不上用 AS 别名而非硬开 FULL。排查「某字段查出是 null」先检查「列名/别名能否匹配属性名(考虑驼峰规则)」。理解「最佳实践:开驼峰映射+简单用 resultType 复杂用 resultMap+保持 PARTIAL 别用 FULL+列名对不上用别名、排查 null 先查列名属性对应」,就掌握了自动映射的实践要点。
记忆钩子:「MyBatis 自动映射=自动把列名对应同名属性(用 resultType 就在用,找不到同名属性就忽略),省去手写 resultMap;user_name→userName 靠 mapUnderscoreToCamelCase=true 驼峰映射(下划线列转驼峰匹配属性,解决 DB 下划线 vs Java 驼峰,Spring Boot 默认开);autoMappingBehavior 三级别:NONE(全关)/PARTIAL(默认,普通列自动嵌套不自动)/FULL(连嵌套也自动有风险,默认不用);匹配靠列名↔属性名对应,对不上就静默 null(排查 null 先查列名),用 AS 别名或 resultMap 解决;简单单表用 resultType、关联嵌套复杂用 resultMap」。
七、常见误区与追问
- 误区:自动映射需要在 resultMap 里配置。 不用——用 resultType 时 MyBatis 就自动把列名对应到同名属性(默认 PARTIAL 级别);resultMap 是「显式映射」,用于自动映射搞不定的复杂场景(关联、嵌套、命名差异大)。
- 误区:user_name 能映射到 userName 是 MyBatis 默认行为。 靠 mapUnderscoreToCamelCase=true(驼峰映射)——原生 MyBatis 默认是 false(不转换),要手动开;只是 mybatis-spring-boot-starter 默认帮你开了,所以 Spring Boot 里感觉是「默认」的。
- 误区:某个字段查出来是 null 是数据库问题。 常见原因是自动映射失败——列名(或别名)和属性名对不上(考虑驼峰规则),MyBatis 静默忽略该列、属性保持默认值 null;排查先看列名/别名能否匹配属性名,用 AS 别名或 resultMap 解决。
- 误区:autoMappingBehavior=FULL 更方便应该开。 FULL 有风险——多表 join 时不同表的同名列可能被错误地自动映射到嵌套对象里(张冠李戴);默认 PARTIAL 不碰嵌套更安全,嵌套映射交给 resultMap 显式配;生产一般不建议开 FULL。
- 追问:自动映射找不到对应属性的列会怎样? 静默忽略(不报错)——MyBatis 把找不到同名属性的列直接跳过,对应属性保持默认值(对象里是 null);这也是「某字段是 null」的常见原因(列名和属性对不上),排查时要留意。
- 追问:resultMap 和自动映射能一起用吗? 能——resultMap 上设 autoMapping=“true”,就在显式映射一部分列(关联、特殊 TypeHandler、命名差异大的)的同时,让其余没显式配的列走自动映射;两者结合减少工作量,兼顾灵活和便利。
- 追问:复杂查询列名对不上,除了 resultMap 还能怎么办? 用 SQL 的 AS 别名让列名匹配属性——SELECT u.name AS userName, COUNT(*) AS total;开了驼峰映射时别名可用下划线(user_name)或直接驼峰(userName);这样简单场景不用写 resultMap 也能自动映射对上。
八、加强记忆
MyBatis 的「自动映射」= 自动把查询结果的列名对应到 Java 对象的同名属性,不用手写 resultMap 每一列(用 resultType 时就在用它;默认规则:每列找同名属性、不区分大小写、找不到就静默忽略)。user_name 能映射到 userName 靠 mapUnderscoreToCamelCase=true(驼峰命名映射)——映射时把下划线列名转成驼峰再匹配属性,解决「DB 习惯下划线、Java 习惯驼峰」的冲突(Spring Boot 默认开启,原生 MyBatis 默认 false)。autoMappingBehavior 三级别:NONE(完全关闭,全显式配)、PARTIAL(默认,自动映射普通列但嵌套部分不自动)、FULL(连嵌套也自动,风险是多表同名列张冠李戴,一般不开)。匹配靠「列名↔属性名」对应,对不上就静默映射不上(属性是 null)——排查「某字段是 null」先看列名/别名能否匹配属性名,用 AS 别名或 resultMap 解决。选择:简单单表用 resultType(自动映射),关联/嵌套/复杂用 resultMap(显式),可混合(resultMap 开 autoMapping)。一句话「自动映射=列名对应同名属性(resultType 就在用,找不到就静默忽略);user_name→userName 靠 mapUnderscoreToCamelCase 驼峰映射(Spring Boot 默认开);autoMappingBehavior:PARTIAL(默认,不碰嵌套)/FULL(嵌套有风险别用);列名对不上静默 null,用 AS 别名或 resultMap;简单用 resultType 复杂用 resultMap」。