全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

多对多关联查询

多对多关系通过中间表(关联表)实现,MyBatis 使用 collection 嵌套映射将关联数据组装为对象集合。

典型场景:学生与课程

SQL
-- 学生表
CREATE TABLE student (id INT PRIMARY KEY, name VARCHAR(50));

-- 课程表
CREATE TABLE course (id INT PRIMARY KEY, name VARCHAR(50));

-- 中间表
CREATE TABLE student_course (
  student_id INT,
  course_id INT,
  PRIMARY KEY (student_id, course_id)
);
Java
public class Student {
  private Integer id;
  private String name;
  private List<Course> courses;
}

public class Course {
  private Integer id;
  private String name;
  private List<Student> students;
}

Join 方式(推荐)

XML
<resultMap id="studentWithCourses" type="Student">
  <id property="id" column="s_id"/>
  <result property="name" column="s_name"/>
  <collection property="courses" ofType="Course">
    <id property="id" column="c_id"/>
    <result property="name" column="c_name"/>
  </collection>
</resultMap>

<select id="selectStudentWithCourses" resultMap="studentWithCourses">
  SELECT s.id s_id, s.name s_name,
         c.id c_id, c.name c_name
  FROM student s
  LEFT JOIN student_course sc ON s.id = sc.student_id
  LEFT JOIN course c ON sc.course_id = c.id
  WHERE s.id = #{id}
</select>

子查询方式

XML
<resultMap id="studentResultMap" type="Student">
  <id property="id" column="id"/>
  <result property="name" column="name"/>
  <collection property="courses"
              column="id"
              ofType="Course"
              select="selectCoursesByStudent"/>
</resultMap>

<select id="selectStudent" resultMap="studentResultMap">
  SELECT * FROM student WHERE id = #{id}
</select>

<select id="selectCoursesByStudent" resultType="Course">
  SELECT c.id, c.name
  FROM course c
  INNER JOIN student_course sc ON c.id = sc.course_id
  WHERE sc.student_id = #{studentId}
</select>

双向关联(级联加载)

同时查询学生的课程和课程下的学生:

XML
<resultMap id="courseWithStudents" type="Course">
  <id property="id" column="c_id"/>
  <result property="name" column="c_name"/>
  <collection property="students" ofType="Student">
    <id property="id" column="s_id"/>
    <result property="name" column="s_name"/>
  </collection>
</resultMap>

<select id="selectCourseWithStudents" resultMap="courseWithStudents">
  SELECT c.id c_id, c.name c_name,
         s.id s_id, s.name s_name
  FROM course c
  LEFT JOIN student_course sc ON c.id = sc.course_id
  LEFT JOIN student s ON sc.student_id = s.id
  WHERE c.id = #{id}
</select>

多层嵌套集合

用户 → 订单 → 商品 的三级嵌套:

XML
<resultMap id="userWithOrders" type="User">
  <id property="id" column="u_id"/>
  <result property="name" column="u_name"/>
  <collection property="orders" ofType="Order">
    <id property="id" column="o_id"/>
    <result property="date" column="o_date"/>
    <collection property="products" ofType="Product">
      <id property="id" column="p_id"/>
      <result property="name" column="p_name"/>
      <result property="price" column="p_price"/>
    </collection>
  </collection>
</resultMap>

<select id="selectUserWithOrders" resultMap="userWithOrders">
  SELECT u.id u_id, u.name u_name,
         o.id o_id, o.date o_date,
         p.id p_id, p.name p_name, p.price p_price
  FROM users u
  LEFT JOIN orders o ON u.id = o.user_id
  LEFT JOIN order_item oi ON o.id = oi.order_id
  LEFT JOIN products p ON oi.product_id = p.id
  WHERE u.id = #{id}
</select>

多层嵌套时,中间表的 JOIN 顺序会影响数据去重,需确保关联键唯一。

Join vs Subquery 对比

维度Join 方式子查询方式
SQL 数量1 条至少 N+1 条
笛卡尔积风险多对多易产生膨胀
性能数据量小时更优数据量大时更可控
延迟加载不支持支持
适用场景数据量可控数据量大或按需加载

多对多关系使用 Join 时,若两端数据量都较大,会产生笛卡尔积膨胀,此时应使用子查询方式。

要点总结

  • 多对多关系通过中间表 + collection 嵌套实现。
  • Join 方式适合数据量可控场景,一次查询完成。
  • 子查询方式支持延迟加载,避免笛卡尔积膨胀。
  • 双向关联需在两端分别配置 collection。
  • 多层嵌套建议不超过三层,否则性能明显下降。
  • 数据量大时优先使用子查询方式。

📝 发现内容有误?点击此处直接编辑

← 上一篇 一对多集合映射
下一篇 → 嵌套查询与 N+1 问题
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库