MyBatis中Collection和Association的底层实现原理

MyBatis中Collection和Association的底层实现原理

Hi 👋, I'm shy

有人见尘埃,有人见星辰

SHY QR Code 技术咨询

引言

在 MyBatis 中,<collection><association> 标签用于处理一对多和一对一的关系。这两个标签在底层通过缓存、对象创建和反射机制,将数据库结果集高效地映射为 Java 对象。本文将重点探讨这两个标签的底层实现原理,并结合实际的数据库示例说明其工作机制。

1. Collection 的底层映射原理

1.1 一对多映射场景

<collection> 标签主要用于表示一对多关系的映射。典型的一对多场景是在一个表中有重复的主对象记录(如用户),但每个主对象包含多个子对象(如订单)。

示例数据库结构

假设我们有以下结果集,表示用户和订单的关系:

user_idusernameorder_idorder_date
1Alice1012023-01-01
1Alice1022023-02-01
2Bob1032023-03-01

这个数据表示,用户 Alice 有两个订单,用户 Bob 有一个订单。我们希望将其映射为一个 User 对象,每个用户对象包含一个订单集合。

MyBatis 映射配置
<resultMap id="userResultMap" type="com.example.User">
    <id property="userId" column="user_id"/>
    <result property="username" column="username"/>
    <collection property="orders" ofType="com.example.Order">
        <id property="orderId" column="order_id"/>
        <result property="orderDate" column="order_date"/>
    </collection>
</resultMap>

在这个映射中,<collection> 标签表示用户对象 User 可能包含多个 Order 对象,ofType 定义了集合中元素的类型。

1.2 底层映射机制

  1. 结果集处理
    当执行 SQL 查询时,MyBatis 会获取完整的结果集。由于用户可能有多个订单,查询结果会包含重复的 user_idusername

  2. 主对象缓存处理
    MyBatis 使用主键 user_id 来判断是否已经创建了对应的 User 对象(如果没有主键, 会使用其他键来模拟唯一键)。

    • 如果 user_id 相同,表示当前行属于同一个用户,则重用该用户对象。
    • 如果 user_id 不同,MyBatis 会创建新的 User 对象。
  3. 集合属性映射
    对于每一行的订单信息(order_idorder_date),MyBatis 会为当前的用户创建新的 Order 对象,并将其添加到用户的 orders 集合中。

  4. 对象重用与集合管理
    通过缓存机制,MyBatis 确保每个用户只创建一次,而订单则根据不同的 order_id 创建。对于相同的用户,每个订单行都会被添加到 orders 集合中。

底层逻辑代码示例(伪代码)
// 查询用户缓存处理
if (!cache.containsKey(userId)) {
    User user = new User();
    user.setUserId(resultSet.getInt("user_id"));
    user.setUsername(resultSet.getString("username"));
    cache.put(userId, user);
}

// 处理订单集合
Order order = new Order();
order.setOrderId(resultSet.getInt("order_id"));
order.setOrderDate(resultSet.getDate("order_date"));

// 将订单添加到用户的集合
user.getOrders().add(order);

MyBatis 会缓存用户对象,并通过 getOrders().add(order) 方法将每个新创建的订单对象加入到用户的订单集合中。

2. Association 的底层映射原理

2.1 一对一映射场景

<association> 标签用于处理一对一或多对一的映射。在这种场景下,通常我们希望将两个表的结果组合为一个主对象和一个关联对象。例如,用户和用户详情是一对一的关系。

示例数据库结构

假设我们有以下结果集,表示用户及其详细信息:

user_idusernamedetail_idageaddress
1Alice125Wonderland
2Bob230Wonderland

这个数据表示,每个用户有一条与之关联的详细信息记录。我们希望将其映射为 User 对象,其中包含一个 UserDetail 对象。

MyBatis 映射配置
<resultMap id="userWithDetailResultMap" type="com.example.User">
    <id property="userId" column="user_id"/>
    <result property="username" column="username"/>
    <association property="detail" javaType="com.example.UserDetail">
        <id property="detailId" column="detail_id"/>
        <result property="age" column="age"/>
        <result property="address" column="address"/>
    </association>
</resultMap>

在这个映射中,<association> 标签用于将用户与详细信息关联起来,javaType 指定了 UserDetail 对象的类型。

2.2 底层映射机制

  1. 主对象创建与缓存
    当执行查询时,MyBatis 会首先根据 user_id 创建或查找缓存中的用户对象。类似于 <collection>,如果用户对象已经存在,则不会重新创建。

  2. 关联对象的映射
    对于每一行,MyBatis 根据 detail_id 创建 UserDetail 对象,并通过 MetaObject 机制,将查询结果的 ageaddress 列映射到 UserDetail 对象中。

  3. 关联对象赋值
    一旦 UserDetail 对象创建完成,MyBatis 会将其赋值给 User 对象的 detail 属性。这是通过反射完成的。

底层逻辑代码示例(伪代码)
// 用户缓存处理
User user = cache.get(userId);

// 创建并映射用户详细信息
UserDetail detail = new UserDetail();
detail.setDetailId(resultSet.getInt("detail_id"));
detail.setAge(resultSet.getInt("age"));
detail.setAddress(resultSet.getString("address"));

// 将详细信息赋值给用户对象
user.setDetail(detail);

MyBatis 在映射时会通过 MetaObject 访问 User 对象的 detail 属性,并使用反射为其赋值。这个过程使得每个用户对象都能够正确关联其详细信息。

3. 总结

MyBatis 中的 <collection><association> 标签通过缓存、反射与对象创建机制,完成数据库结果集到 Java 对象的映射。在处理一对多和一对一关系时,这两个标签的底层机制可以高效地组织复杂的对象结构。

  • <collection> 标签:通过主对象的缓存和子对象集合的动态添加,实现了复杂的一对多关系的映射。
  • <association> 标签:通过关联对象的创建和属性映射,实现了一对一或多对一的关联映射。

理解这些底层机制能够帮助开发者优化查询性能,并更好地设计数据结构,充分发挥 MyBatis 的 ORM 功能。


欢迎评论区沟通交流~

MyBatiscollection和association都是用来处理关联关系的元素,但是它们的作用和用法有所不同。 1. collection:用于处理一对多的关联关系,即一个实体对象包含多个子实体对象的情况。collection标签通常用于在一个实体类包含一个List或Set类型的属性,该属性包含多个子实体对象。示例: ```xml <resultMap id="authorMap" type="Author"> <id property="id" column="author_id"/> <result property="username" column="username"/> <result property="password" column="password"/> <collection property="articles" ofType="Article"> <id property="id" column="article_id"/> <result property="title" column="title"/> <result property="content" column="content"/> </collection> </resultMap> ``` 2. association:用于处理一对一的关联关系,即一个实体对象包含一个子实体对象的情况。association标签通常用于在一个实体类包含一个其他实体类类型的属性。示例: ```xml <resultMap id="articleMap" type="Article"> <id property="id" column="article_id"/> <result property="title" column="title"/> <result property="content" column="content"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="username"/> <result property="password" column="password"/> </association> </resultMap> ``` 总结: collection和association都是用于处理关联关系的元素,但是collection用于处理一对多的关联关系,association用于处理一对一的关联关系。在具体使用时,需要根据实际情况选择合适的标签。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shy好好学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值