Mybatis结果集自动映射文档查询及字段映射异常求助
我尝试查找MyBatis将ResultSet自动映射为对象的官方文档,但未找到相关细节,仅在社区讨论中发现过相关问题。我原本对MyBatis映射逻辑的理解是:
- 优先查找无参构造器,通过反射直接访问字段赋值,无需依赖setter方法
- 如果没有无参构造器,则使用多参构造器,按构造器参数声明顺序完成映射(构造器自动映射)
但这个解释无法解决我遇到的实际问题:使用@Data注解的MyUserDTO通过MyBatis Mapper映射时,数据库查询结果中address字段为null,但最终映射后的对象里,address字段却被注入了age字段的值。
代码示例
DTO类
@Data class MyUserDTO { private Long id; private String name; private Integer age; private String address; }
Mapper接口
@Mapper interface UserMapper { @Select("SELECT * FROM user_table WHERE id = #{id}") MyUserDTO findByUserId(@Param("id") Long id); }
数据库查询及结果
执行查询语句:SELECT * FROM user_table WHERE id = 1;
返回结果:
id | name | age | address ------------------------- 1 | jin | 20 | null
调用findByUserId(1)后,userDTO.getAddress()返回"20"而非预期的null。
环境信息
- Spring Boot 2.7.8
- MyBatis 3.5.11(已配置
map-underscore-to-camel-case: true) - SQLite 3
- AdoptOpenJDK 11.0.11
- macOS Ventura v13.2.1
问题原因分析
这个问题的核心是Lombok的@Data注解生成的构造器与MyBatis构造器自动映射逻辑冲突。
虽然@Data注解默认会生成无参构造器,但部分场景下,Lombok字节码生成的细节会导致MyBatis误判为需要使用构造器自动映射,而非直接访问字段赋值。再加上SQLite JDBC驱动返回ResultSet的列顺序可能与DTO字段声明顺序不一致,MyBatis在构造器映射时按参数位置而非名称匹配,最终导致age的值被错误赋值给address字段。
另外,MyBatis的自动映射逻辑会优先使用构造器(如果它认为可以用的话),而非直接字段访问,这进一步触发了顺序匹配的错误。
解决方案
显式添加无参构造器
在MyUserDTO上添加@NoArgsConstructor注解,明确告诉MyBatis优先使用无参构造器+字段直接赋值:@Data @NoArgsConstructor class MyUserDTO { private Long id; private String name; private Integer age; private String address; }关闭构造器自动映射
在MyBatis配置中强制禁用构造器映射,确保MyBatis始终使用字段直接访问:mybatis: configuration: use-constructor-mappings: false显式指定查询列名
避免使用SELECT *,明确写出查询列并保持与DTO字段顺序一致,同时避免不必要的字段:@Select("SELECT id, name, age, address FROM user_table WHERE id = #{id}") MyUserDTO findByUserId(@Param("id") Long id);
内容的提问来源于stack exchange,提问作者Jinwoo An

