MyBatis 关联查询延迟加载实战案例

 更新时间:2026年05月08日 15:21:35   作者:Java成神之路-  
本文介绍了MyBatis开发中一对多、一对一关联查询的配置方法,重点讲解了resultMap、association、collection等核心标签的使用场景和用语法,并详细解释了延迟加载(fetchType属性)的配置方法和执行流程,最后总结了单表查询、一对一关联和一对多关联的配置方法和注意事项

前言

在MyBatis开发中,单表查询我们直接用resultType就能轻松搞定,但遇到一对多、一对一关联查询(如1个用户对应多个订单、1个订单对应1个用户),且需要优化性能时,就必须用到resultMap配合延迟加载

一、核心基础概念

1. 业务场景:一对多关系

最经典的场景:用户表(一) ↔ 订单表(多)

  • 1个用户可以拥有多个订单
  • 1个订单只属于1个用户

对应Java实体类设计:

// 用户实体(一的一方)
@Data
public class User {
    private Integer id;
    private String username;
    // 一对多:存放当前用户的所有订单
    private List<Order> orderList;
}
// 订单实体(多的一方)
@Data
public class Order {
    private Integer id;
    private String orderNo;
    // 多对一:存放订单所属的用户
    private User user;
}

2. 立即加载 VS 延迟加载(懒加载)

这是性能优化的核心,也是配置fetchType="lazy"的意义:

加载方式含义优缺点
立即加载查询数据时,同时查询关联数据代码简单;查询冗余,性能差
延迟加载查询时只查本身数据,用到关联数据时才查询减少冗余SQL,性能高;需要额外配置
通俗理解:延迟加载就是按需加载,用不到的数据绝不查询!

二、为什么要写 resultMap?(关联查询专用)

1. 单表查询

日常单查用户/订单,一行代码搞定,完全不用复杂配置:

<!-- 单表查询:resultType 直接映射,无需配置resultMap -->
<select id="selectById" resultType="com.itheima.pojo.User">
    select * from tb_user where id = #{id}
</select>

✅ 适用场景:纯单表查询,无关联关系

2. 必须用复杂配置的场景

当需求变为:查询主数据 + 按需加载关联数据

MyBatis规定:一对多/一对一关联 + 延迟加载,必须使用 resultMap 配置

这是固定语法!

三、一对一延迟加载(association 标签)

一对一场景:订单表 ↔ 用户表(1个订单只属于1个用户),MyBatis用association标签实现一对一延迟加载!

1. 一对一核心配置

<?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.itheima.mapper.OrderMapper">
    <!-- 一对一结果映射 -->
    <resultMap id="orderResultMap" type="order" autoMapping="true">
        <id property="id" column="id"/>
        <result property="orderNo" column="order_no"/>
        <!-- 一对一关联配置:延迟加载用户信息 -->
        <association
            property="user"
            javaType="user"
            select="com.itheima.mapper.UserMapper.selectById"
            column="user_id"
            fetchType="lazy">
        </association>
    </resultMap>
    <!-- 根据id查询订单 -->
    <select id="selectById" resultMap="orderResultMap">
        select * from tb_order where id = #{id}
    </select>
</mapper>

✨ association 标签5大核心属性

  1. property:实体类中关联对象属性名(Order类中的user)
  2. javaType:关联对象的实体类型
  3. select:延迟加载调用的查询方法全路径
  4. column:传递给子查询的参数(订单表的user_id)
  5. fetchType="lazy":开启一对一延迟加载

四、一对多延迟加载(collection 标签)

<?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.itheima.mapper.UserMapper">
    <!-- 1. 定义复杂映射规则:id唯一标识,type映射实体类,autoMapping自动匹配字段 -->
    <resultMap id="userResultMap" type="user" autoMapping="true">
        <!-- 2. 主键映射(规范写法,可省略) -->
        <id property="id" column="id"/>
        <!-- 普通字段映射(autoMapping=true可自动匹配,可省略) -->
        <result property="username" column="username"/>
        <!-- 3. 核心:一对多关联映射,延迟加载配置 -->
        <collection
            property="orderList"
            ofType="order"
            select="com.itheima.mapper.OrderMapper.findByUid"
            column="id"
            fetchType="lazy">
        </collection>
    </resultMap>
    <!-- 4. 查询用户,绑定上面的映射规则 -->
    <select id="selectById" resultMap="userResultMap">
        select * from tb_user where id = #{id}
    </select>
</mapper>

✨ collection 标签5大核心属性

这是一对多配置的灵魂,记住这5个属性,再也不会写错:

  1. property:实体类中集合属性的名称(必须和User类中的orderList一致)
  2. ofType:集合中存储的实体类型
  3. select:延迟加载时执行的子查询方法全路径
  4. column:传递给子查询的参数列(用户id)
  5. fetchType="lazy"开启延迟加载(默认是立即加载)

五、完整实战代码

1. MyBatis全局配置(开启延迟加载总开关)

<settings>
    <!-- 开启延迟加载总开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 关闭积极加载(3.4.1后默认false,配置更保险) -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

lazyLoadingEnabled(总开关)

作用:控制 是否开启延迟加载

默认值:false → 关闭延迟加载,默认使用立即加载

人话:查询主数据时,直接把关联数据一起查出来,不等待调用

aggressiveLazyLoading(触发方式)

作用:控制 延迟加载的触发条件

默认值:false

人话:只有开启了延迟加载(lazyLoadingEnabled=true),这个配置才有用!

true:调用对象任何方法(toString/equals/getter)都触发加载

false:只调用关联属性的 getter 方法才触发加载(最标准的懒加载)

2. 子查询Mapper配置

一对多/一对一依赖的子查询方法必须单独定义:

<mapper namespace="com.itheima.mapper.OrderMapper">
    <!-- 根据用户id查询订单:供一对多延迟加载调用 -->
    <select id="findByUid" resultType="com.itheima.pojo.Order">
        select * from tb_order where user_id = #{userId}
    </select>
</mapper>

3. Mapper接口

// UserMapper
public interface UserMapper {
    User selectById(Integer id);
}
// OrderMapper
public interface OrderMapper {
    List<Order> findByUid(Integer userId);
    Order selectById(Integer id);
}

六、延迟加载执行流程

1. 一对一执行流程

@Test
public void testOneToOne(){
    // 1. 仅查询订单:只执行1条SQL → select * from tb_order where id=1
    Order order = orderMapper.selectById(1);
    System.out.println("订单编号:" + order.getOrderNo());
    // 2. 调用user:触发一对一延迟加载,查询用户信息
    System.out.println("订单所属用户:" + order.getUser());
}

2. 一对多执行流程

@Test
public void testLazyLoad(){
    // 1. 仅查询用户:只执行1条SQL → select * from tb_user where id=1
    User user = userMapper.selectById(1);
    System.out.println("查询到用户:" + user.getUsername());
    // 2. 未使用订单数据:不执行订单查询SQL
    // 3. 调用orderList:触发延迟加载,执行订单SQL → select * from tb_order where user_id=1
    System.out.println("用户订单:" + user.getOrderList());
}

完美实现按需加载

七、注意事项

  1. 忘记开启全局延迟加载
    全局开关 lazyLoadingEnabled(默认 false)控制所有未显式设置 fetchType 的关联; 局部属性 fetchType="lazy" 可以独立让单个关联延迟加载,不受全局开关影响。
  2. 子查询方法不存在/路径写错
    select属性必须是全限定名,因为这是两个mapper.xml文件。
  3. 实体类属性名不匹配
    配置的property属性必须和实体类属性名完全一致。
  4. 标签区分
    一对多用collection,一对一用association,不可混用!

八、延迟加载的优势

  • 提升性能:避免查询不需要的关联数据,减少数据库压力
  • 解决N+1问题:查询数据时,不会自动查询关联数据
  • 灵活适配业务:只查主数据时,无冗余SQL
  • 通用适配:同时支持一对多、一对一关联场景

九、总结

  1. 单表查询:用resultType,无需任何复杂配置(日常开发主流);
  2. 一对一关联:必须用resultMap + association
  3. 一对多关联:必须用resultMap + collection
  4. 延迟加载核心fetchType="lazy" + 全局配置开关 + 按需加载;
  5. 核心标签:association(一对一)、collection(一对多),语法结构高度一致,仅标签和类型属性不同。

到此这篇关于MyBatis 关联查询延迟加载案例的文章就介绍到这了,更多相关mybatis 延迟加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 为zookeeper配置相应的acl权限

    为zookeeper配置相应的acl权限

    这篇文章主要介绍了为zookeeper配置相应的acl权限的相关实例,具有一定参考价值,需要的朋友可以了解下。
    2017-09-09
  • 超好用的Java工具类库Hutool用法详解

    超好用的Java工具类库Hutool用法详解

    Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,下面就跟随小编一起来学习一下Hutool的具体用法吧
    2023-09-09
  • mybatis接收以逗号分隔的字符串批量查询方式

    mybatis接收以逗号分隔的字符串批量查询方式

    这篇文章主要介绍了mybatis接收以逗号分隔的字符串批量查询方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • java生成json实现隐藏掉关键属性

    java生成json实现隐藏掉关键属性

    这篇文章主要介绍了java生成json实现隐藏掉关键属性,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java中文分词之正向最大匹配法实例代码

    java中文分词之正向最大匹配法实例代码

    中文分词应用很广泛,网上也有很多开源项目,下面这篇文章主要给大家介绍了关于java中文分词之正向最大匹配法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-11-11
  • idea集成shell运行环境以及shell输出中文乱码的解决

    idea集成shell运行环境以及shell输出中文乱码的解决

    这篇文章主要介绍了idea集成shell运行环境以及shell输出中文乱码的解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Java中不得不知的Collection接口与Iterator迭代器

    Java中不得不知的Collection接口与Iterator迭代器

    这篇文章主要介绍了Java中的Collection接口与Iterator迭代器,文中有详细的代码示例供大家参考,对我们的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-06-06
  • jdk8 FunctionalInterface注解源码解读

    jdk8 FunctionalInterface注解源码解读

    这篇文章主要介绍了jdk8 FunctionalInterface注解源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • 详细介绍MyBatis 3.4.0版本的功能

    详细介绍MyBatis 3.4.0版本的功能

    这篇文章主要给大家介绍了关于MyBatis 3.4.0版本的功能,文中只列举部分重要的内容,详细内容看官方说明,需要的朋友可以参考借鉴,下面跟着小编一起来学习学习吧。
    2017-06-06
  • 微服务mybatis typehandler使用详解(就这一篇够了)

    微服务mybatis typehandler使用详解(就这一篇够了)

    TypeHandler是MyBatis框架的核心组件,实现数据库表字段类型和Java 数据类型之间的相互转换,本文介绍通过实例代码mybatis typehandler使用,感兴趣的朋友一起看看吧
    2024-02-02

最新评论