MyBatis 中,关联查询(Association Mapping)用于在查询中映射多个表之间的关系,常见的有以下三种类型:

  • 一对一(One-to-One):一个对象与另一个对象之间存在唯一对应关系
  • 一对多(One-to-Many):一个对象包含多个对象
  • 多对多(Many-to-Many):两个对象之间通过中间表存在多对多的关系

MyBatis 通过 resultMapassociationcollection 标签来配置关联查询,并将查询结果自动映射为 Java 对象。

🏠 1. 一对一关联查询

场景:

  • 一个 User 关联一个 Address
  • User 表中的 address_id 外键关联 Address 表的 id

① 建表示例

CREATE TABLE User (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    address_id INT,
    FOREIGN KEY (address_id) REFERENCES Address(id)
);

CREATE TABLE Address (
    id INT PRIMARY KEY,
    city VARCHAR(50),
    street VARCHAR(100)
);

② 定义 Java 类

User.java

public class User {
    private int id;
    private String name;
    private Address address;
    // getter & setter
}

Address.java

public class Address {
    private int id;
    private String city;
    private String street;
    // getter & setter
}

③ Mapper 映射文件

使用 <association> 进行一对一映射:

<mapper namespace="com.example.mapper.UserMapper">
    <resultMap id="userResultMap" type="User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="address" column="address_id" javaType="Address" select="getAddressById"/>
    </resultMap>

    <!-- 获取用户信息 -->
    <select id="getUserById" resultMap="userResultMap">
        SELECT id, name, address_id FROM User WHERE id = #{id}
    </select>

    <!-- 通过 id 查询地址 -->
    <select id="getAddressById" resultType="Address">
        SELECT id, city, street FROM Address WHERE id = #{id}
    </select>
</mapper>

④ Mapper 接口

public interface UserMapper {
    User getUserById(int id);
}

🔥 说明

  • <association>:定义一对一关联
  • column="address_id":传递到 select="getAddressById" 作为参数
  • select="getAddressById":表示通过 address_id 进行二次查询

⑤ SQL 执行过程

  1. 执行 getUserById() 方法
  2. 通过 address_id 触发 getAddressById 查询
  3. 将查询结果封装成 Address 对象并注入到 User 对象中

🏢 2. 一对多关联查询

场景:

  • 一个 Department 下包含多个 Employee
  • Employee 表中的 department_id 外键关联 Department 表的 id

① 建表示例

CREATE TABLE Department (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);

CREATE TABLE Employee (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    FOREIGN KEY (department_id) REFERENCES Department(id)
);

② 定义 Java 类

Department.java

public class Department {
    private int id;
    private String name;
    private List<Employee> employees;
    // getter & setter
}

Employee.java

public class Employee {
    private int id;
    private String name;
    private int departmentId;
    // getter & setter
}

③ Mapper 映射文件

使用 <collection> 进行一对多映射:

<mapper namespace="com.example.mapper.DepartmentMapper">
    <resultMap id="departmentResultMap" type="Department">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="employees" ofType="Employee">
            <id property="id" column="employee_id"/>
            <result property="name" column="employee_name"/>
        </collection>
    </resultMap>

    <!-- 查询部门及其员工 -->
    <select id="getDepartmentById" resultMap="departmentResultMap">
        SELECT d.id, d.name, e.id AS employee_id, e.name AS employee_name
        FROM Department d
        LEFT JOIN Employee e ON d.id = e.department_id
        WHERE d.id = #{id}
    </select>
</mapper>

④ Mapper 接口

public interface DepartmentMapper {
    Department getDepartmentById(int id);
}

🔥 说明

  • <collection>:定义一对多关联
  • ofType="Employee":表示集合中对象类型为 Employee
  • 通过 LEFT JOIN 关联查询

⑤ SQL 执行过程

  1. 执行 getDepartmentById() 方法
  2. 通过 LEFT JOIN 生成完整结果集
  3. 将结果集封装成 Department 对象,并填充 employees 列表

🔗 3. 多对多关联查询

场景:

  • 一个学生对应多个课程,一个课程也对应多个学生
  • 使用中间表 student_course 维护多对多关系

① 建表示例

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),
    FOREIGN KEY (student_id) REFERENCES Student(id),
    FOREIGN KEY (course_id) REFERENCES Course(id)
);

② 定义 Java 类

Student.java

public class Student {
    private int id;
    private String name;
    private List<Course> courses;
    // getter & setter
}

Course.java

public class Course {
    private int id;
    private String name;
    // getter & setter
}

③ Mapper 映射文件

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

<select id="getStudentById" resultMap="studentResultMap">
    SELECT id, name FROM Student WHERE id = #{id}
</select>

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

🔥 说明

  • <collection> 定义集合映射
  • select="getCoursesByStudentId" 触发二次查询
  • 多对多关系由中间表维护

🚀 4. 总结

关联关系关键标签触发方式
一对一<association>通过外键或 JOIN 查询
一对多<collection>通过 JOIN 查询
多对多<collection>通过中间表和二次查询

✅ 推荐在 多对多一对多 关系中使用 延迟加载(lazy loading) 来提升性能! 😎