MyBatis如何用@Results实现一对多映射(不使用@One/@Many)
不用@One/@Many,单SQL+@Results实现一对多映射
核心思路
通过**SQL关联查询(左连接/内连接)**一次性查出主表+子表的所有数据,再通过@Results配置主实体的唯一标识(id=true),让MyBatis自动将同主实体的多条子记录合并为集合,无需嵌套查询(也就不会生成多条SQL)。
具体实现步骤
1. 定义实体类
以用户(User)和用户订单(UserOrder)为例,User包含一对多关联的订单集合:
public class User { private Long id; private String username; // 一对多关联的订单集合 private List<UserOrder> orders; // getter/setter 省略 } public class UserOrder { private Long id; private String orderNo; private Long userId; // getter/setter 省略 }
2. 编写关联查询SQL与@Results映射
在Mapper接口中定义查询方法,用左连接一次性拉取用户和对应所有订单,直接在同一个@Results里完成主、子实体的映射,无需额外子查询方法:
@Select("SELECT u.id as user_id, u.username, o.id as order_id, o.order_no, o.user_id " + "FROM user u LEFT JOIN user_order o ON u.id = o.user_id " + "WHERE u.id = #{userId}") @Results(id = "UserWithOrdersResultMap", value = { // 标记主实体User的唯一id,MyBatis靠这个识别同一条主记录,合并子数据 @Result(column = "user_id", property = "id", id = true), @Result(column = "username", property = "username"), // 直接映射集合内子实体的属性,无需指定select方法 @Result(column = "order_id", property = "orders.id"), @Result(column = "order_no", property = "orders.orderNo"), @Result(column = "user_id", property = "orders.userId") }) User getUserWithOrders(@Param("userId") Long userId);
3. 关键注意点
- 必须标记主实体的唯一id:
@Result(..., id = true)是MyBatis合并同主实体多条子记录的核心,没有这个配置会生成多个重复的主实体对象。 - 避免列名冲突:SQL中给重复的列(如主表id和子表id)起别名(如
user_id、order_id),防止映射时混淆。 - 无需额外子查询方法:你之前写的
selectUserProfile()属于冗余代码,单SQL查询下直接在同一@Results内映射子实体属性即可。
常见问题排查
如果出现集合为空或主实体重复的情况:
- 检查主实体的
id=true标记是否正确配置。 - 核对SQL的关联条件是否正确,确保子表外键与主表主键关联无误。
- 确认
@Result的column属性与SQL中的列名(含别名)完全匹配。
内容的提问来源于stack exchange,提问作者1515biubiubiu
相关产品推荐
相关产品推荐

