在实际开发中,数据库表之间通常存在复杂的关联关系。MyBatis 通过 <resultMap> 提供了强大的对象关系映射能力,支持 一对一(1:1)、一对多(1:N)、多对一(N:1) 和 多对多(N:N) 四种经典关系的映射。本文将结合代码示例,逐一讲解其实现方式。
一、一对一(1:1)映射
场景:一个用户对应一个身份证信息(User ↔ IdCard)。
当 Java 对象属性名与数据库字段名不一致(如字段为 user_id,属性为 id),或需要显式控制映射逻辑时,应使用 <resultMap>。
<!-- UserMapper.xml --> <resultMap id="userWithIdCard" type="User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <!-- 一对一关联 --> <association property="idCard" javaType="IdCard"> <id property="id" column="card_id"/> <result property="number" column="card_number"/> </association> </resultMap> <select id="selectUserWithIdCard" resultMap="userWithIdCard"> SELECT u.user_id, u.username, c.card_id, c.card_number FROM user u LEFT JOIN id_card c ON u.user_id = c.user_id WHERE u.user_id = #{id} </select>
✅ 说明:
- 使用
<association>标签映射“一”的对象;- 若字段与属性名一致(且开启驼峰命名),可直接用
resultType,无需resultMap。
二、一对多(1:N)映射
场景:一个用户拥有多个角色(User → List)。
在“一”的一方(User)中定义集合属性,并使用 <collection> 标签映射。
// User.java public class User { private String id; private String username; private List<Role> roles; // 一对多 // getter/setter... }
<!-- UserMapper.xml --> <resultMap id="userWithRoles" type="User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <!-- 一对多 --> <collection property="roles" ofType="Role"> <id property="id" column="role_id"/> <result property="name" column="role_name"/> </collection> </resultMap> <select id="selectUserWithRoles" resultMap="userWithRoles"> SELECT u.user_id, u.username, r.role_id, r.role_name FROM user u LEFT JOIN user_role ur ON u.user_id = ur.user_id LEFT JOIN role r ON ur.role_id = r.role_id WHERE u.user_id = #{id} </select>
📌 查询结果示例:
{ "id": "1003", "username": "小波", "roles": [ {"id": "1", "name": "开发"}, {"id": "2", "name": "TL"} ] }
✅ 关键点:
- 使用
ofType指定集合中元素的类型;- SQL 需通过 JOIN 查询出所有关联数据,MyBatis 会自动按主对象分组聚合。
三、多对一(N:1)映射
场景:多篇博客属于同一个作者(Blog → Author)。
在“多”的一方(Blog)中引用“一”的对象(Author),使用 <association>。
<!-- BlogMapper.xml --> <resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id"/> <result property="title" column="blog_title"/> <!-- 多对一:每篇博客关联一个作者 --> <association property="author" column="author_id" javaType="Author" resultMap="authorResult"/> </resultMap> <resultMap id="authorResult" type="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> </resultMap> <select id="selectBlogWithAuthor" resultMap="blogResult"> SELECT b.blog_id, b.blog_title, a.author_id, a.author_username FROM blog b LEFT JOIN author a ON b.author_id = a.author_id WHERE b.blog_id = #{id} </select>
✅ 两种写法:
- 引用外部
resultMap(如上)——推荐,复用性强;- 内联定义
<association>内部字段(适用于简单场景):
<association property="author" javaType="Author"> <id column="author_id" property="id"/> <result column="author_username" property="username"/> </association>
四、多对多(N:N)映射
场景:用户与部门互相关联(User ↔ Dept),通过中间表 user_dept 实现。
由于 MyBatis 不直接支持 N:N 自动映射,需借助中间实体类或两次查询实现。常用做法是:
- 在 User 中定义
List<Dept>; - 在 Dept 中定义
List<User>; - 通过中间表 JOIN 查询,利用
<collection>聚合。
// User.java public class User { private String id; private String name; private List<Dept> depts; // 多对多 }
<!-- UserMapper.xml --> <resultMap id="userWithDepts" type="User"> <id property="id" column="user_id"/> <result property="name" column="user_name"/> <collection property="depts" ofType="Dept"> <id property="id" column="dept_id"/> <result property="name" column="dept_name"/> </collection> </resultMap> <select id="selectUserWithDepts" resultMap="userWithDepts"> SELECT u.user_id, u.user_name, d.dept_id, d.dept_name FROM user u LEFT JOIN user_dept ud ON u.user_id = ud.user_id LEFT JOIN dept d ON ud.dept_id = d.dept_id WHERE u.user_id = #{id} </select>
⚠️ 注意:
- 多对多本质是 两个一对多 的组合;
- 若关联数据量大,建议分步查询(先查主对象,再根据 ID 批量查关联对象),避免笛卡尔积膨胀。
总结
| 关系类型 | Java 属性 | XML 标签 | 说明 |
| 一对一(1:1) | 单个对象 | <association> |
如 User → IdCard |
| 一对多(1:N) | List<T> |
<collection> |
如 User → List |
| 多对一(N:1) | 单个对象 | <association> |
如 Blog → Author |
| 多对多(N:N) | List<T> |
<collection> |
通过中间表实现 |
合理使用 <resultMap>、<association> 和 <collection>,可以高效完成复杂对象关系的映射,让 MyBatis 成为你数据层的强大助力。