MyBatis-Plus(简称 MP)作为 MyBatis 的增强工具,在简化 CRUD 操作的同时,也保留了 MyBatis 强大的结果映射能力。ResultMap
是 MyBatis/MyBatis-Plus 中用于将数据库结果集映射到 Java 对象的核心机制,尤其适用于复杂查询、字段与属性名不一致、多表关联、嵌套对象等场景。
一、核心概念
1. ResultMap 是什么?
ResultMap
是 MyBatis 提供的一个强大的结果映射配置,用于定义数据库列(column)与 Java 对象属性(property)之间的映射关系。它比自动映射更灵活、更可控。
2. 为什么需要 ResultMap?
- 数据库字段名与 Java 属性名不一致(如
user_name
→userName
) - 多表联查,结果需要映射到嵌套对象(如
User
包含Role
) - 处理
null
值的特殊逻辑 - 枚举类型、自定义类型处理器(TypeHandler)
- 避免 N+1 查询问题(通过
association
和collection
)
3. MyBatis-Plus 与 ResultMap 的关系
- MP 默认使用自动映射(Auto Mapping),适用于简单场景。
- 当自动映射无法满足需求时,可手动定义
ResultMap
。 - MP 完全兼容 MyBatis 的
ResultMap
机制。
二、操作步骤(非常详细)
步骤 1:创建实体类(POJO)
// 用户实体
public class User {
private Long id;
private String userName;
private Integer age;
private String email;
private Role role; // 嵌套对象
// getter/setter 省略
}
// 角色实体
public class Role {
private Long id;
private String roleName;
private String description;
// getter/setter 省略
}
步骤 2:编写 Mapper 接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 自定义方法,使用 ResultMap
List<User> selectUserWithRole(@Param("roleId") Long roleId);
}
步骤 3:编写 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">
<!-- 定义 ResultMap -->
<resultMap id="UserWithRoleResultMap" type="User">
<!-- 基本字段映射 -->
<id property="id" column="user_id" />
<result property="userName" column="user_name" />
<result property="age" column="age" />
<result property="email" column="email" />
<!-- 嵌套对象映射 -->
<association property="role" javaType="Role">
<id property="id" column="role_id" />
<result property="roleName" column="role_name" />
<result property="description" column="role_desc" />
</association>
</resultMap>
<!-- 查询语句,使用 resultMap -->
<select id="selectUserWithRole" resultMap="UserWithRoleResultMap">
SELECT
u.id AS user_id,
u.user_name,
u.age,
u.email,
r.id AS role_id,
r.role_name,
r.description AS role_desc
FROM user u
LEFT JOIN role r ON u.role_id = r.id
<where>
<if test="roleId != null">
AND r.id = #{roleId}
</if>
</where>
</select>
</mapper>
步骤 4:在 Service 层调用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getUsersWithRole(Long roleId) {
return userMapper.selectUserWithRole(roleId);
}
}
步骤 5:验证结果
调用 getUsersWithRole(1L)
,返回的 User
列表中每个 User
对象的 role
属性已被正确填充。
三、常见错误与解决方案
错误现象 | 原因 | 解决方案 |
---|---|---|
字段未映射,值为 null |
property 或 column 名称拼写错误 |
检查 resultMap 中的 property 和 column 是否正确 |
嵌套对象为 null |
association 的 column 没有对应数据库字段 |
确保 SQL 查询返回了嵌套对象所需的所有字段 |
ResultMap 找不到 |
resultMap 的 id 不存在或拼写错误 |
检查 resultMap 的 id 与 select 标签中的 resultMap 属性是否一致 |
类型转换异常 | 数据库类型与 Java 类型不匹配 | 使用 typeHandler 或检查字段类型 |
N+1 查询性能差 |
使用了 select 方式嵌套查询 |
改用 join 查询 + resultMap 关联映射 |
四、注意事项
命名规范:
- 数据库字段建议使用下划线命名(如
user_name
)。 - Java 属性使用驼峰命名(如
userName
)。 - MP 默认开启
mapUnderscoreToCamelCase
,可自动映射,但复杂场景仍需ResultMap
。
- 数据库字段建议使用下划线命名(如
主键映射:
- 使用
<id>
标签映射主键,有助于 MyBatis 优化缓存和结果处理。
- 使用
避免循环引用:
- 如果
User
包含Role
,而Role
又包含List<User>
,需注意避免无限递归。
- 如果
XML 文件位置:
- 确保
UserMapper.xml
在resources/mapper/
目录下,并在application.yml
中配置:mybatis-plus: mapper-locations: classpath:mapper/*.xml
- 确保
五、使用技巧
1. 重用 ResultMap
可以定义基础 ResultMap
,并通过 <extends>
继承:
<resultMap id="BaseUserResultMap" type="User">
<id property="id" column="user_id" />
<result property="userName" column="user_name" />
<result property="age" column="age" />
</resultMap>
<resultMap id="UserWithRoleResultMap" type="User" extends="BaseUserResultMap">
<association property="role" javaType="Role">
<id property="id" column="role_id" />
<result property="roleName" column="role_name" />
</association>
</resultMap>
2. 使用自动映射 + 手动补充
<resultMap id="PartialResultMap" type="User" autoMapping="true">
<association property="role" javaType="Role" autoMapping="true">
<id property="id" column="role_id" />
</association>
</resultMap>
autoMapping="true"
表示未明确映射的字段尝试自动映射。
3. 处理枚举类型
public enum Status {
ACTIVE(1), INACTIVE(0);
private int code;
// 构造函数、getter
}
<result property="status" column="status" typeHandler="com.example.enums.StatusTypeHandler"/>
六、最佳实践与性能优化
✅ 最佳实践
- 优先使用 MP 自动映射:简单场景无需
ResultMap
。 - 复杂查询使用
ResultMap
:多表关联、嵌套对象、字段不一致。 - 合理使用
association
和collection
:避免N+1
查询。 - 命名清晰:
resultMap
的id
应具有业务含义,如UserWithRoleResultMap
。 - 避免过度嵌套:层级不宜过深,影响可读性和性能。
⚡ 性能优化
使用
JOIN
而非嵌套SELECT
:<!-- ❌ 避免 --> <association property="role" column="role_id" select="selectRoleById" /> <!-- ✅ 推荐 --> <association property="role" javaType="Role"> <id property="id" column="role_id" /> ... </association>
只查询必要字段:避免
SELECT *
,减少网络传输。合理使用缓存:MyBatis 一级/二级缓存可提升重复查询性能。
批量处理:使用
collection
映射集合时,可结合fetchType="lazy"
实现懒加载。
七、总结
场景 | 推荐方案 |
---|---|
简单 CRUD | MP 自动映射 |
字段名不一致 | resultMap + result |
多表关联 | resultMap + association /collection |
枚举/复杂类型 | resultMap + typeHandler |
性能敏感 | JOIN + resultMap ,避免 N+1 |
掌握 ResultMap
是深入使用 MyBatis-Plus 的关键技能之一。它赋予你对结果映射的完全控制权,是处理复杂业务查询的利器。