🎬 HoRain 云小助手:个人主页
⛺️生活的理想,就是为了理想的生活!
⛳️ 推荐
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
目录
⛳️ 推荐
🔎 三种关联关系的实现
1. 一对一映射
2. 一对多映射
3. 多对多映射
⚖️ 两种策略对比与性能考量
💡 实践建议
MyBatis的关联关系映射是其最强大的特性之一,它能将数据库中的表关联关系优雅地映射到Java对象模型上。下面通过一个表格和详细说明来帮你全面掌握这个概念。
关联类型 | 适用场景 | MyBatis 标签 | Java 属性类型 | 关键属性 |
|---|---|---|---|---|
一对一 ( | 一个人对应一个身份证 |
| 单个复杂对象 |
|
一对多 ( | 一个部门对应多个员工 |
| 集合 (如 |
|
多对多 ( | 一个学生对应多门课程,一门课程对应多个学生 |
| 集合 (如 |
|
🔎 三种关联关系的实现
1. 一对一映射
一对一关系很常见,比如一个人(Person)对应一张身份证(IdCard)。在MyBatis中,使用<association>标签来处理,主要有两种实现方式。
嵌套结果映射(推荐,一次性查询)
这种方式通过一个多表连接查询(JOIN)一次性获取所有数据,性能较好 。
<resultMap id="PersonResultMap" type="Person"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- 使用 association 映射一对一关系 --> <association property="card" javaType="IdCard"> <id property="id" column="card_id"/> <!-- 注意这里对应查询结果中的列名 --> <result property="code" column="card_code"/> </association> </resultMap> <select id="selectPersonWithCard" resultMap="PersonResultMap"> SELECT p.id, p.name, c.id as card_id, c.code as card_code FROM person p LEFT JOIN id_card c ON p.card_id = c.id WHERE p.id = #{id} </select>嵌套查询(分步查询)
这种方式先查询主对象(
Person),然后根据外键执行另一个查询(select语句)来获取关联对象(IdCard)。<resultMap id="PersonResultMap" type="Person"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- select 属性指定另一个查询的 id,column 指定传递给该查询的参数 --> <association property="card" column="card_id" javaType="IdCard" select="selectIdCard"/> </resultMap> <select id="selectPersonById" resultMap="PersonResultMap"> SELECT * FROM person WHERE id = #{id} </select> <select id="selectIdCard" resultType="IdCard"> SELECT * FROM id_card WHERE id = #{card_id} <!-- 参数来自上层查询的 card_id 列 --> </select>
2. 一对多映射
一对多关系也很普遍,比如一个用户(User)有多个订单(Order)。这时需要使用<collection>标签 。
<resultMap id="UserResultMap" type="User"> <id property="id" column="id"/> <result property="username" column="username"/> <!-- 使用 collection 映射一对多关系,ofType 指定集合中元素的类型 --> <collection property="ordersList" ofType="Order"> <id property="id" column="order_id"/> <result property="number" column="order_number"/> </collection> </resultMap> <select id="selectUserWithOrders" resultMap="UserResultMap"> SELECT u.id, u.username, o.id as order_id, o.number as order_number FROM user u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id = #{id} </select>对应的Java类如下:
public class User { private Integer id; private String username; private List<Order> ordersList; // 一对多关系,使用集合 // ... getters and setters }3. 多对多映射
多对多关系(如订单和商品)需要通过一个中间表来维护。在映射时,本质上可以看作是两个一对多关系的组合,因此仍然使用<collection>标签 。
<resultMap id="OrderResultMap" type="Order"> <id property="id" column="id"/> <result property="number" column="number"/> <!-- 通过中间表关联到商品 --> <collection property="productList" ofType="Product"> <id property="id" column="product_id"/> <result property="name" column="product_name"/> <result property="price" column="price"/> </collection> </resultMap> <select id="selectOrderWithProducts" resultMap="OrderResultMap"> SELECT o.id, o.number, p.id as product_id, p.name as product_name, p.price FROM orders o LEFT JOIN order_item oi ON o.id = oi.order_id -- 通过中间表关联 LEFT JOIN product p ON oi.product_id = p.id -- 关联到商品表 WHERE o.id = #{id} </select>⚖️ 两种策略对比与性能考量
嵌套结果映射和嵌套查询两种策略各有优劣,是理解和优化MyBatis关联查询的关键 。
特性 | 嵌套结果映射 | 嵌套查询 |
|---|---|---|
SQL数量 | 1条(多表JOIN) | N+1条(1条主查询 + N条关联查询) |
性能 | 高(网络开销小) | 潜在风险(可能引发N+1查询问题) |
代码复杂度 | SQL较复杂 | SQL简单,配置清晰 |
延迟加载 | 不支持 | 支持,可有效缓解N+1问题 |
N+1查询问题是嵌套查询模式的主要风险。当查询一个用户列表(返回N条记录)时,如果立即加载每个用户的订单,就会产生1(查询用户)+ N(查询每个用户的订单)条SQL语句,对数据库造成巨大压力 。
解决方案是启用延迟加载。在MyBatis配置文件中(mybatis-config.xml)进行如下设置:
<settings> <!-- 开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 按需加载,避免加载所有关联对象 --> <setting name="aggressiveLazyLoading" value="false"/> </settings>开启后,关联对象(如订单)只有在代码真正访问它(如调用user.getOrdersList())时才会被查询,从而避免不必要的数据库访问 。
💡 实践建议
首选嵌套结果映射:对于关联数据量不大、且需要立即使用的场景,优先使用嵌套结果映射(JOIN查询)以获得最佳性能 。
善用延迟加载:对于关联数据量大、或不是每次都需要的场景(如查询用户基本信息时不一定需要他的订单列表),使用嵌套查询并开启延迟加载 。
注意列名别名:在多表JOIN时,如果不同表有相同列名(如
id),务必使用别名(AS)区分,否则会导致映射错误 。考虑使用注解:对于简单的关联映射,可以使用
@Results,@One,@Many等注解在Mapper接口上直接配置,使代码更简洁 。
希望这份详细的解释能帮助你更好地理解和应用MyBatis的关联映射。如果你在实际编码中遇到具体问题,比如某个配置不生效,我很乐意帮你进一步分析。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙