MyBatis 关联映射详解:1:1、1:N、N:1 与 N:N 实现方式

简介: 本文详解 MyBatis 中 resultMap 的四种关联映射:一对一、一对多、多对一和多对多,结合代码示例讲解实现方式与最佳实践,助你掌握复杂对象关系的数据映射技巧。

在实际开发中,数据库表之间通常存在复杂的关联关系。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>

两种写法

  1. 引用外部 resultMap(如上)——推荐,复用性强;
  2. 内联定义 <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 自动映射,需借助中间实体类两次查询实现。常用做法是:

  1. 在 User 中定义 List<Dept>
  2. 在 Dept 中定义 List<User>
  3. 通过中间表 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 成为你数据层的强大助力。


相关文章
|
1天前
|
JSON 前端开发 安全
用自定义注解 + 拦截器实现登录鉴权
通过自定义注解 `@Login` 结合 Spring 拦截器,实现声明式登录校验。无需重复编码,自动拦截未登录请求,提升代码可维护性与安全性,适用于前后端分离架构的权限控制实践。
|
1天前
|
测试技术
OAuth2.0测试
本节演示授权码模式测试流程:通过访问指定URL跳转至SpringSecurity登录页,使用系统账户登录后,授权read/write权限,获取授权码并申请访问令牌token,完成OAuth2认证全过程。
OAuth2.0测试
|
1天前
|
存储 安全 数据安全/隐私保护
认识OAuth2.0
OAuth2.0是一种开放授权协议,允许第三方应用在用户授权下安全访问资源,无需获取用户账号密码。其核心是通过令牌(token)机制实现权限控制,广泛用于第三方登录、服务间资源调用等场景。支持多种授权模式,其中授权码模式最安全,适用于Web和移动应用,如微信、QQ登录即基于此模式实现。
认识OAuth2.0
|
1天前
|
安全 Java 数据库
RememberMe基本用法
本文介绍Spring Security中RememberMe功能的实现原理及优化方案。通过配置自动登录,用户勾选后可持久化会话,避免重复登录。系统通过remember-me令牌识别用户,但存在安全风险。为提升安全性,建议将Token持久化至数据库,并增加二次校验机制,防止令牌泄露导致的安全问题。
RememberMe基本用法
|
1天前
|
数据安全/隐私保护
RememberMe简介及用法
RememberMe功能并非简单保存用户名密码,而是服务器端维持登录状态的机制。用户关闭浏览器后重新打开,仍可保持登录,提升体验,区别于传统Session会话需重复登录的方式。
|
1天前
|
安全 Java 数据库
自定义认证实现流程
本文介绍如何在Spring Security中实现自定义用户认证。首先创建自定义UserDetailsService,实现loadUserByUsername方法,从数据库加载用户信息与权限;然后在SecurityConfig中注册该服务,配置登录逻辑与权限规则。通过整合持久层,完成基于数据库的认证流程,重启项目后即可生效。
自定义认证实现流程
|
1天前
|
存储 JSON NoSQL
MongoDB 文档的增删改查实战
本文以“文章评论”为例,介绍MongoDB文档的增删改查操作。涵盖单条与批量插入、条件查询、投影筛选、局部更新与原子增减,以及安全删除等核心操作,助你掌握高效数据管理技巧。
|
1天前
|
缓存 安全 数据挖掘
数据库设计三范式:从理论到实战
本文深入浅出讲解数据库三范式(1NF、2NF、3NF),通过真实案例解析各范式的核心要求与常见误区,强调“范式是工具而非教条”,帮助开发者在规范性与性能间找到平衡,构建合理、易维护的数据模型。
|
1天前
|
存储 NoSQL 关系型数据库
MongoDB 数据库与集合操作详解
本文介绍MongoDB中数据库与集合的基本操作。数据按“数据库→集合→文档”层级组织。通过`use`创建或切换数据库,`show dbs`查看列表,`db.dropDatabase()`删除数据库。集合无需显式创建,插入文档时自动生,也可用`createCollection`手动创建。使用`show collections`查看集合,`drop()`删除集合。注意命名规范及保留名如admin、local等。
|
1天前
|
SQL 安全 Java
了解 SQL 注入:原理、危害与防御
SQL注入是Web安全头号威胁,攻击者通过恶意SQL代码窃取、篡改或删除数据。本文详解其原理、危害及防御方案,强调预编译、输入校验、最小权限等核心防护措施,助你筑牢数据库安全防线。