2282 字
约 7 分钟
14
结果映射
无标签

第08章 结果映射

学习 ResultMap 的使用和复杂结果集的映射处理
学习进度:32% 代码演示按钮 💻 查看完整代码 - 在线IDE体验

🎯 学习目标

📚

理解核心概念

掌握本章的核心概念和基本原理
🔧

掌握实践技能

学会相关的配置方法和实践技巧
💡

了解应用场景

明确在实际项目中的应用场景和最佳实践

学习目标

  • 深入理解MyBatis结果映射的工作原理
  • 掌握基本结果映射和自定义结果映射
  • 学会配置一对一和一对多关联映射
  • 理解嵌套查询和嵌套结果的区别
  • 掌握延迟加载的配置和使用
  • 学会处理复杂的多层嵌套映射

结果映射概述

结果映射是MyBatis最强大的特性之一,它负责将SQL查询结果转换为Java对象。MyBatis提供了灵活的映射机制,支持简单类型映射、复杂对象映射、关联对象映射等多种场景。

结果映射的类型

  • 基本映射:直接使用resultType
  • 自定义映射:使用resultMap定义复杂映射
  • 关联映射:处理对象之间的关联关系
  • 集合映射:处理一对多的集合关系

基本结果映射

最简单的结果映射方式,直接使用resultType指定返回类型。

简单类型映射

<!-- 返回基本类型 -->
<select id="countUsers" resultType="long">
    SELECT COUNT(*) FROM users
</select>

<!-- 返回字符串 -->
<select id="getUserName" resultType="string">
    SELECT name FROM users WHERE id = #{id}
</select>

对象类型映射

<!-- 返回单个对象 -->
<select id="selectUserById" resultType="User">
    SELECT id, name, age, email FROM users WHERE id = #{id}
</select>

<!-- 返回对象列表 -->
<select id="selectAllUsers" resultType="User">
    SELECT id, name, age, email FROM users
</select>

自动映射规则

  • 列名与属性名完全匹配
  • 开启驼峰命名转换(mapUnderscoreToCamelCase=true)
  • 类型自动转换(String ↔ 数值类型等)

自定义结果映射

当自动映射无法满足需求时,可以使用resultMap定义自定义的结果映射。

基本ResultMap

<resultMap id="userResultMap" type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <result property="age" column="user_age"/>
    <result property="email" column="user_email"/>
</resultMap>

<select id="selectUsersWithCustomMapping" resultMap="userResultMap">
    SELECT id as user_id, name as user_name, 
           age as user_age, email as user_email 
    FROM users
</select>

ResultMap元素说明

  • id:主键映射,用于性能优化
  • result:普通字段映射
  • association:一对一关联映射
  • collection:一对多集合映射
  • discriminator:鉴别器,根据条件选择映射

一对一关联映射

使用association标签处理一对一的关联关系。

嵌套查询方式

<resultMap id="userWithProfileMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="email" column="email"/>
    <association property="profile" column="id" 
                 select="com.example.mybatis.mapper.UserProfileMapper.selectProfileByUserId"/>
</resultMap>

<select id="selectUsersWithProfile" resultMap="userWithProfileMap">
    SELECT id, name, age, email FROM users
</select>

嵌套结果方式

<resultMap id="userWithProfileNestedMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="email" column="email"/>
    <association property="profile" javaType="UserProfile">
        <id property="id" column="profile_id"/>
        <result property="userId" column="user_id"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
        <result property="birthday" column="birthday"/>
        <result property="avatar" column="avatar"/>
    </association>
</resultMap>

<select id="selectUsersWithProfileNested" resultMap="userWithProfileNestedMap">
    SELECT u.id, u.name, u.age, u.email,
           p.id as profile_id, p.user_id, p.phone, 
           p.address, p.birthday, p.avatar
    FROM users u
    LEFT JOIN user_profiles p ON u.id = p.user_id
</select>

两种方式的比较

特性 嵌套查询 嵌套结果
SQL数量 多个SQL(N+1问题) 单个SQL
性能 可能较慢 通常更快
延迟加载 支持 不支持
配置复杂度 简单 相对复杂

一对多集合映射

使用collection标签处理一对多的集合关系。

嵌套查询方式

<resultMap id="userWithOrdersMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="email" column="email"/>
    <collection property="orders" column="id"
                select="com.example.mybatis.mapper.OrderMapper.selectOrdersByUserId"/>
</resultMap>

<select id="selectUsersWithOrders" resultMap="userWithOrdersMap">
    SELECT id, name, age, email FROM users
</select>

嵌套结果方式

<resultMap id="userWithOrdersNestedMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="email" column="email"/>
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="userId" column="order_user_id"/>
        <result property="orderNo" column="order_no"/>
        <result property="amount" column="amount"/>
        <result property="status" column="status"/>
        <result property="createTime" column="create_time"/>
    </collection>
</resultMap>

<select id="selectUsersWithOrdersNested" resultMap="userWithOrdersNestedMap">
    SELECT u.id, u.name, u.age, u.email,
           o.id as order_id, o.user_id as order_user_id, 
           o.order_no, o.amount, o.status, o.create_time
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    ORDER BY u.id, o.id
</select>

复杂嵌套映射

在实际项目中,经常需要同时处理多种关联关系。

用户+档案+订单的复杂映射

<resultMap id="userComplexMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="email" column="email"/>
    
    <!-- 一对一关联:用户档案 -->
    <association property="profile" javaType="UserProfile">
        <id property="id" column="profile_id"/>
        <result property="userId" column="user_id"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
        <result property="birthday" column="birthday"/>
        <result property="avatar" column="avatar"/>
    </association>
    
    <!-- 一对多关联:用户订单 -->
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="userId" column="order_user_id"/>
        <result property="orderNo" column="order_no"/>
        <result property="amount" column="amount"/>
        <result property="status" column="status"/>
        <result property="createTime" column="create_time"/>
    </collection>
</resultMap>

<select id="selectUsersWithComplexMapping" resultMap="userComplexMap">
    SELECT u.id, u.name, u.age, u.email,
           p.id as profile_id, p.user_id, p.phone, p.address, p.birthday, p.avatar,
           o.id as order_id, o.user_id as order_user_id, o.order_no, 
           o.amount, o.status, o.create_time
    FROM users u
    LEFT JOIN user_profiles p ON u.id = p.user_id
    LEFT JOIN orders o ON u.id = o.user_id
    ORDER BY u.id, o.id
</select>

延迟加载

延迟加载是一种性能优化技术,只有在真正访问关联对象时才执行查询。

全局配置

<!-- 在mybatis-config.xml中配置 -->
<settings>
    <!-- 开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 关闭积极的延迟加载 -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

局部配置

<resultMap id="userLazyMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="email" column="email"/>
    
    <!-- 延迟加载用户档案 -->
    <association property="profile" column="id" fetchType="lazy"
                 select="com.example.mybatis.mapper.UserProfileMapper.selectProfileByUserId"/>
    
    <!-- 延迟加载用户订单 -->
    <collection property="orders" column="id" fetchType="lazy"
                select="com.example.mybatis.mapper.OrderMapper.selectOrdersByUserId"/>
</resultMap>

fetchType属性

  • eager:立即加载
  • lazy:延迟加载
  • 默认值:根据全局配置决定

高级映射技巧

1. 使用构造方法映射

<resultMap id="userConstructorMap" type="User">
    <constructor>
        <idArg column="id" javaType="long"/>
        <arg column="name" javaType="string"/>
        <arg column="age" javaType="int"/>
        <arg column="email" javaType="string"/>
    </constructor>
</resultMap>

2. 鉴别器映射

<resultMap id="userDiscriminatorMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <discriminator javaType="string" column="user_type">
        <case value="VIP" resultMap="vipUserMap"/>
        <case value="NORMAL" resultMap="normalUserMap"/>
    </discriminator>
</resultMap>

3. 继承映射

<resultMap id="baseUserMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="email" column="email"/>
</resultMap>

<resultMap id="extendedUserMap" type="User" extends="baseUserMap">
    <association property="profile" column="id" 
                 select="selectProfileByUserId"/>
</resultMap>

性能优化建议

1. 选择合适的映射方式

  • 简单查询使用resultType
  • 复杂映射使用resultMap
  • 一对一关系优先使用嵌套结果
  • 一对多关系根据数据量选择

2. 避免N+1问题

  • 合理使用嵌套结果映射
  • 启用延迟加载
  • 批量查询优化
  • 缓存机制配合

3. 延迟加载最佳实践

  • 只对大对象或集合使用延迟加载
  • 避免在循环中触发延迟加载
  • 注意Session生命周期
  • 合理设置fetchType

4. 内存使用优化

  • 避免加载不必要的字段
  • 合理设计对象结构
  • 及时释放大对象
  • 使用分页查询

常见问题与解决方案

Q: 延迟加载时出现LazyInitializationException?

A: 确保在Session关闭前访问延迟加载的属性,或者使用OpenSessionInView模式。

Q: 一对多映射时数据重复?

A: 检查resultMap中的id配置是否正确,确保主键映射准确。

Q: 嵌套结果映射性能差?

A: 考虑使用嵌套查询+延迟加载,或者优化SQL查询和索引。

Q: 复杂映射配置错误?

A: 逐步构建映射,先测试简单映射再添加复杂关联。

Q: 类型转换异常?

A: 检查javaType和jdbcType配置,确保类型匹配。

实践练习

练习1:基本映射

创建一个自定义ResultMap,处理列名与属性名不匹配的情况。

练习2:一对一映射

实现用户和用户档案的一对一映射,分别使用嵌套查询和嵌套结果两种方式。

练习3:一对多映射

实现用户和订单的一对多映射,观察数据的组织方式。

练习4:延迟加载

配置延迟加载,观察SQL执行时机和性能差异。

练习5:复杂映射

实现包含用户、档案、订单的三层嵌套映射。

运行示例程序

步骤1:导入项目

将chapter-08-result-mapping.json导入到IntelliJ IDEA中。

步骤2:配置数据库

确保MySQL数据库运行正常,执行init.sql创建测试数据。

步骤3:运行程序

运行ResultMappingDemo.java,观察各种结果映射的执行效果。

步骤4:观察SQL日志

查看控制台输出的SQL语句,理解不同映射方式的查询策略。

步骤5:测试延迟加载

观察延迟加载的触发时机和SQL执行情况。

步骤6:运行测试

执行ResultMappingTest.java中的测试用例,验证各种映射功能。
本章小结

📖 本章小结

本章我们学习了 结果映射 的相关内容,包括:

  • 核心概念和基本原理
  • 配置方法和实践技巧
  • 应用场景和最佳实践
  • 常见问题和解决方案

下一章节 🎉

恭喜完成本章学习!

你已经掌握了 结果映射 的核心知识
第09章缓存机制继续学习 MyBatis 的更多特性和应用⏱️预计 30 分钟📊中等难度开始学习 → ← 第07章 📚 课程目录 第09章 →

结果映射
https://clxhxhhr.top/posts/2850/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。