当前位置: 首页 > news >正文

MyBatis 中 resultMap、association、collection标签详解

MyBatis 中 resultMap、association、collection标签详解

  • MyBatis 中 `<resultMap>`、`<association>` 与 `<collection>` 标签详解
    • 🧱 前提:假设的业务模型
      • 数据库表结构(简化版)
      • 对应的 Java 实体类(简化)
    • 1️⃣ `<resultMap>`:结果映射的根标签
      • 作用
      • 示例:基础 resultMap(无关联)
    • 2️⃣ `<association>`:处理一对一关联
      • 作用
      • SQL 查询(连接查询)
      • 对应的 `<resultMap>`
    • 3️⃣ `<collection>`:处理一对多关联
      • 作用
      • SQL 查询(连接查询)
      • 对应的 `<resultMap>`
    • 🔁 补充:嵌套查询(N+1 问题)
    • ✅ 总结对比
  • 🔍 什么是 N+1 问题?
    • 🧩 举个例子:订单与用户(一对一)
      • ❌ 使用嵌套查询(引发 N+1)
      • 执行过程:
    • ⚠️ N+1 问题的危害
    • ✅ 如何避免 N+1 问题?
      • ✅ 推荐方案:使用 **JOIN 查询 + `<association>` / `<collection>`**
    • 🆚 嵌套查询 vs JOIN 查询 对比
    • 🔒 补充:MyBatis 的懒加载(Lazy Loading)
    • ✅ 总结


MyBatis 中 <resultMap><association><collection> 标签详解

在 MyBatis 中,<resultMap><association><collection> 是处理复杂对象映射(尤其是对象之间存在一对一、一对多关系)时非常关键的标签。本文将逐一解释每个标签的作用,并配合具体的 SQL 示例和 Java 实体类结构,帮助你深入理解。


🧱 前提:假设的业务模型

我们以一个常见的“订单-用户-订单项”模型为例:

  • User(用户):一对一关联订单(一个用户可以有多个订单,但这里我们先演示一对一)
  • Order(订单):属于一个用户(一对一),包含多个订单项(一对多)
  • OrderItem(订单项):属于一个订单

数据库表结构(简化版)

-- 用户表
CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(50)
);-- 订单表
CREATE TABLE orders (id INT PRIMARY KEY,user_id INT,order_no VARCHAR(50)
);-- 订单项表
CREATE TABLE order_item (id INT PRIMARY KEY,order_id INT,product_name VARCHAR(100),quantity INT
);

对应的 Java 实体类(简化)

public class User {private Integer id;private String name;// getter/setter
}public class Order {private Integer id;private String orderNo;private User user;               // 一对一:订单属于一个用户private List<OrderItem> items;   // 一对多:订单包含多个订单项// getter/setter
}public class OrderItem {private Integer id;private String productName;private Integer quantity;// getter/setter
}

1️⃣ <resultMap>:结果映射的根标签

作用

定义如何将数据库查询结果(ResultSet)映射到 Java 对象。可以包含普通字段映射、<association>(一对一)、<collection>(一对多)等。

示例:基础 resultMap(无关联)

<resultMap id="BaseOrderMap" type="Order"><id property="id" column="id"/><result property="orderNo" column="order_no"/>
</resultMap>

这个只是映射 Order 自身的字段,不涉及关联对象。


2️⃣ <association>:处理一对一关联

作用

用于映射单个对象属性(如 Order 中的 User)。

SQL 查询(连接查询)

SELECT o.id AS order_id,o.order_no,u.id AS user_id,u.name AS user_name
FROM orders o
JOIN user u ON o.user_id = u.id
WHERE o.id = #{orderId}

对应的 <resultMap>

<resultMap id="OrderWithUserMap" type="Order"><id property="id" column="order_id"/><result property="orderNo" column="order_no"/><!-- 一对一:关联 User 对象 --><association property="user" javaType="User"><id property="id" column="user_id"/><result property="name" column="user_name"/></association>
</resultMap><select id="getOrderWithUser" resultMap="OrderWithUserMap">SELECT o.id AS order_id,o.order_no,u.id AS user_id,u.name AS user_nameFROM orders oJOIN user u ON o.user_id = u.idWHERE o.id = #{orderId}
</select>

✅ 这样 MyBatis 就会自动将 user_iduser_name 映射到 Order.user 属性中。


3️⃣ <collection>:处理一对多关联

作用

用于映射集合属性(如 Order 中的 List<OrderItem>)。

SQL 查询(连接查询)

SELECT o.id AS order_id,o.order_no,u.id AS user_id,u.name AS user_name,oi.id AS item_id,oi.product_name,oi.quantity
FROM orders o
JOIN user u ON o.user_id = u.id
LEFT JOIN order_item oi ON o.id = oi.order_id
WHERE o.id = #{orderId}

注意:这里使用 LEFT JOIN,因为订单可能没有订单项。

对应的 <resultMap>

<resultMap id="OrderWithUserAndItemsMap" type="Order"><id property="id" column="order_id"/><result property="orderNo" column="order_no"/><!-- 一对一:User --><association property="user" javaType="User"><id property="id" column="user_id"/><result property="name" column="user_name"/></association><!-- 一对多:OrderItem 列表 --><collection property="items" ofType="OrderItem"><id property="id" column="item_id"/><result property="productName" column="product_name"/><result property="quantity" column="quantity"/></collection>
</resultMap><select id="getOrderWithDetails" resultMap="OrderWithUserAndItemsMap">SELECT o.id AS order_id,o.order_no,u.id AS user_id,u.name AS user_name,oi.id AS item_id,oi.product_name,oi.quantityFROM orders oJOIN user u ON o.user_id = u.idLEFT JOIN order_item oi ON o.id = oi.order_idWHERE o.id = #{orderId}
</select>

✅ MyBatis 会自动将多个 item_id 行聚合成一个 List<OrderItem>,并赋值给 Order.items

⚠️ 注意:使用 <collection> 时,必须确保主对象(Order)的主键(如 order_id)在结果集中,MyBatis 才能正确分组聚合子对象。


🔁 补充:嵌套查询(N+1 问题)

除了上面的“连接查询”方式,MyBatis 也支持通过嵌套 select 实现关联(但可能引发 N+1 查询问题):

<resultMap id="OrderWithUserNested" type="Order"><id property="id" column="id"/><result property="orderNo" column="order_no"/><association property="user" column="user_id" select="com.example.mapper.UserMapper.selectUserById"/>
</resultMap><select id="selectUserById" resultType="User">SELECT * FROM user WHERE id = #{id}
</select>

这种方式虽然清晰,但每查一个订单就要再查一次用户,效率较低。推荐优先使用连接查询 + <association>/<collection> 的方式


✅ 总结对比

标签用途对应关系示例属性
<resultMap>定义结果映射规则id, type
<association>映射单个对象一对一property, javaType
<collection>映射对象集合一对多property, ofType

当然可以!下面是对 MyBatis 嵌套查询中的 N+1 问题 的详细解释,包括其成因、影响、示例以及解决方案。


🔍 什么是 N+1 问题?

N+1 问题 是指在执行数据库查询时,先执行 1 次主查询,然后对每一条结果再执行 1 次子查询,总共执行 1 + N 次 SQL(N 是主查询返回的记录数)。

这在 ORM(如 MyBatis、Hibernate)中处理关联关系时非常常见,尤其是在使用 嵌套查询(Nested Select) 方式映射一对一或一对多关系时。


🧩 举个例子:订单与用户(一对一)

假设我们要查询 所有订单及其对应的用户信息

❌ 使用嵌套查询(引发 N+1)

MyBatis 映射如下:

<resultMap id="OrderWithUserNested" type="Order"><id property="id" column="id"/><result property="orderNo" column="order_no"/><!-- 嵌套查询:每查一个订单,就调用一次 selectUserById --><association property="user"column="user_id"select="com.example.mapper.UserMapper.selectUserById"/>
</resultMap><select id="selectAllOrders" resultMap="OrderWithUserNested">SELECT id, order_no, user_id FROM orders
</select><select id="selectUserById" resultType="User">SELECT id, name FROM user WHERE id = #{id}
</select>

执行过程:

  1. 第 1 次查询(主查询)

    SELECT id, order_no, user_id FROM orders;
    

    假设返回了 100 条订单记录(N = 100)。

  2. 接下来执行 100 次子查询(每条订单查一次用户):

    SELECT id, name FROM user WHERE id = 1;
    SELECT id, name FROM user WHERE id = 2;
    ...
    SELECT id, name FROM user WHERE id = 100;
    

✅ 总共执行了 1 + 100 = 101 次 SQL 查询

这就是典型的 N+1 问题


⚠️ N+1 问题的危害

  • 性能严重下降:大量数据库往返通信(round-trip),增加延迟。
  • 数据库压力大:短时间内执行大量相似 SQL,可能拖垮数据库。
  • 网络开销高:尤其在分布式系统中,影响更明显。

✅ 如何避免 N+1 问题?

✅ 推荐方案:使用 JOIN 查询 + <association> / <collection>

将关联数据通过 一次 JOIN 查询 获取,MyBatis 自动映射。

<resultMap id="OrderWithUserMap" type="Order"><id property="id" column="order_id"/><result property="orderNo" column="order_no"/><association property="user" javaType="User"><id property="id" column="user_id"/><result property="name" column="user_name"/></association>
</resultMap><select id="selectAllOrdersWithUser" resultMap="OrderWithUserMap">SELECT o.id AS order_id,o.order_no,u.id AS user_id,u.name AS user_nameFROM orders oJOIN user u ON o.user_id = u.id
</select>

只执行 1 次 SQL,高效且安全。

💡 对于一对多(如订单与订单项),同样适用,只需配合 <collection> 使用 LEFT JOIN


🆚 嵌套查询 vs JOIN 查询 对比

特性嵌套查询(Nested Select)JOIN 查询
SQL 执行次数1 + N(N+1 问题)1
性能差(尤其 N 很大时)
SQL 复杂度简单(每个查询都很简单)稍复杂(需处理列别名、去重)
内存占用低(按需加载)高(结果集可能重复膨胀)
适用场景数据量小、懒加载需求绝大多数关联查询场景

📌 MyBatis 官方推荐:除非明确需要懒加载(lazy loading),否则优先使用 JOIN + <association>/<collection>


🔒 补充:MyBatis 的懒加载(Lazy Loading)

MyBatis 支持通过配置开启懒加载,此时嵌套查询不会立即执行,而是在访问关联属性时才触发子查询

<!-- mybatis-config.xml -->
<settings><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/>
</settings>

这样可以缓解 N+1 问题(因为不是所有关联都会被访问),但一旦访问多个对象的关联属性,仍可能触发多次查询,所以仍需谨慎使用。


✅ 总结

  • N+1 问题:1 次主查询 + N 次子查询,性能杀手。
  • 根本原因:在映射关联关系时使用了嵌套 select
  • 最佳实践:使用 JOIN 查询 + <association> / <collection> 一次性获取所有数据。
  • 例外情况:仅在需要懒加载且关联数据访问频率很低时,才考虑嵌套查询。

🎯 记住一句话
“能用 JOIN 解决的关联查询,就不要用嵌套 select。”


http://www.dtcms.com/a/569226.html

相关文章:

  • 网站长期建设运营计划书自己怎么健网站视频下载
  • 网站强制qq弹窗代码专业网页设计制作价格
  • QuickRedis
  • 微信小程序开发案例 | 个人相册小程序(上)
  • JAVA多商户家政同城上门服务预约服务抢单派单+自营商城系统支持小程序+APP+公众号+h5
  • ELK 学习笔记
  • 在 Ubuntu 上快速配置 Node.js 环境(附问题说明)
  • discuz修改网站关键词wordpress微信qq登陆
  • 钦州公司做网站网络空间安全专业大学排名
  • ELK 企业级日志分析系统部署与实践
  • AI研究-119 DeepSeek-OCR PyTorch FlashAttn 2.7.3 推理与部署 模型规模与资源详细分析
  • 1.3.课设实验-数据结构-栈、队列-银行叫号系统
  • 网站如何做监测链接做问卷赚钱的网站
  • 做网站好还是做app好人工智能建筑设计软件
  • 云计算——虚拟化介绍
  • 电力电子技术 第十四章——AC/AC转换器
  • MIT-归并排序和快速排序
  • 乐鑫ESP32-C2小尺寸高性价比,物联网应用的理想无线连接方案
  • 1.4.课设实验-数据结构-单链表-文教文化用品品牌2.0
  • 广州网站制作公司排名wordpress切换语言实现
  • 门户网站开发需求分析建设网站图片高清
  • PHP 表单 - 验证邮件和URL
  • 自己感觉好点的东西
  • 计算机网络自顶向下方法30——运输层 网络拥塞控制中的公平性
  • 专门做隐形眼镜的网站seo的中文含义是什么意思
  • O2O行业风口下的运营策略与定制开发AI智能名片S2B2C商城小程序的应用研究
  • 新建代码仓库后,初始化仓库
  • 龙川做网站的wordpress 文章折叠
  • Glances服务器硬件资源监控工具
  • Docker(三)_容器打包