MyBatis-Plus(简称 MP)作为 MyBatis 的增强工具,在简化 CRUD 操作的同时,也保留了 MyBatis 强大的结果映射能力。ResultMap 是 MyBatis/MyBatis-Plus 中用于将数据库结果集映射到 Java 对象的核心机制,尤其适用于复杂查询、字段与属性名不一致、多表关联、嵌套对象等场景。


一、核心概念

1. ResultMap 是什么?

ResultMap 是 MyBatis 提供的一个强大的结果映射配置,用于定义数据库列(column)与 Java 对象属性(property)之间的映射关系。它比自动映射更灵活、更可控。

2. 为什么需要 ResultMap?

  • 数据库字段名与 Java 属性名不一致(如 user_nameuserName
  • 多表联查,结果需要映射到嵌套对象(如 User 包含 Role
  • 处理 null 值的特殊逻辑
  • 枚举类型、自定义类型处理器(TypeHandler)
  • 避免 N+1 查询问题(通过 associationcollection

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 propertycolumn 名称拼写错误 检查 resultMap 中的 propertycolumn 是否正确
嵌套对象为 null associationcolumn 没有对应数据库字段 确保 SQL 查询返回了嵌套对象所需的所有字段
ResultMap 找不到 resultMapid 不存在或拼写错误 检查 resultMapidselect 标签中的 resultMap 属性是否一致
类型转换异常 数据库类型与 Java 类型不匹配 使用 typeHandler 或检查字段类型
N+1 查询性能差 使用了 select 方式嵌套查询 改用 join 查询 + resultMap 关联映射

四、注意事项

  1. 命名规范

    • 数据库字段建议使用下划线命名(如 user_name)。
    • Java 属性使用驼峰命名(如 userName)。
    • MP 默认开启 mapUnderscoreToCamelCase,可自动映射,但复杂场景仍需 ResultMap
  2. 主键映射

    • 使用 <id> 标签映射主键,有助于 MyBatis 优化缓存和结果处理。
  3. 避免循环引用

    • 如果 User 包含 Role,而 Role 又包含 List<User>,需注意避免无限递归。
  4. XML 文件位置

    • 确保 UserMapper.xmlresources/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"/>

六、最佳实践与性能优化

✅ 最佳实践

  1. 优先使用 MP 自动映射:简单场景无需 ResultMap
  2. 复杂查询使用 ResultMap:多表关联、嵌套对象、字段不一致。
  3. 合理使用 associationcollection:避免 N+1 查询。
  4. 命名清晰resultMapid 应具有业务含义,如 UserWithRoleResultMap
  5. 避免过度嵌套:层级不宜过深,影响可读性和性能。

⚡ 性能优化

  1. 使用 JOIN 而非嵌套 SELECT

    <!-- ❌ 避免 -->
    <association property="role" column="role_id" 
                 select="selectRoleById" />
    
    <!-- ✅ 推荐 -->
    <association property="role" javaType="Role">
        <id property="id" column="role_id" />
        ...
    </association>
    
  2. 只查询必要字段:避免 SELECT *,减少网络传输。

  3. 合理使用缓存:MyBatis 一级/二级缓存可提升重复查询性能。

  4. 批量处理:使用 collection 映射集合时,可结合 fetchType="lazy" 实现懒加载。


七、总结

场景 推荐方案
简单 CRUD MP 自动映射
字段名不一致 resultMap + result
多表关联 resultMap + association/collection
枚举/复杂类型 resultMap + typeHandler
性能敏感 JOIN + resultMap,避免 N+1

掌握 ResultMap 是深入使用 MyBatis-Plus 的关键技能之一。它赋予你对结果映射的完全控制权,是处理复杂业务查询的利器。