以下是对 MyBatis 中关系映射更详细的解答:

1. 一对一 (One-to-One) 关系映射

嵌套结果映射(XML 方式):

  • resultMap 元素:

    • resultMap 是核心元素,用于定义如何将查询结果映射到 Java 对象。
    • id 属性为结果映射提供唯一标识符,方便在 select 语句中引用。
    • type 属性指定结果映射对应的 Java 类型。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    <resultMap id="userResultMap" type="com.example.User">
        <id property="id" column="user_id"/>
        <result property="name" column="user_name"/>
        <association property="address" javaType="com.example.Address">
            <id property="id" column="address_id"/>
            <result property="street" column="street"/>
            <result property="city" column="city"/>
        </association>
    </resultMap>
    
    • <id> 元素:用于映射主键,有助于 MyBatis 进行对象的唯一性识别。
    • <result> 元素:映射非主键列到对象的属性。
    • <association> 元素:用于处理一对一关系,将另一个对象(在此为 Address)映射到 User 对象的 address 属性。
      • property 属性指定 Java 对象中的属性名。
      • javaType 属性指定关联对象的 Java 类型。
  • select 语句:

    • select 语句中使用 resultMap 而不是 resultType 来引用结果映射。
    1
    2
    3
    4
    5
    6
    7
    
    <select id="getUserById" resultMap="userResultMap">
        SELECT u.id as user_id, u.name as user_name,
               a.id as address_id, a.street, a.city
        FROM users u
        JOIN addresses a ON u.address_id = a.id
        WHERE u.id = #{id}
    </select>
    
    • 这里使用了表连接将 users 表和 addresses 表关联,通过 ON 子句建立关系。

使用 @One 注解(Java 接口方式):

  • Java 类:

    1
    2
    3
    4
    5
    6
    7
    
    public class User {
        private int id;
        private String name;
        @One(select = "com.example.AddressMapper.getAddressById", fetch = FetchType.EAGER)
        private Address address;
        // Getter 和 Setter 方法
    }
    
    • @One 注解:
      • select 属性指定一个全限定的 SQL 映射语句的名称,该语句将返回关联对象(在此为 Address)。
      • fetch 属性指定加载策略,FetchType.EAGER 表示立即加载,FetchType.LAZY 表示延迟加载。

2. 一对多 (One-to-Many) 关系映射

嵌套结果映射(XML 方式):

  • resultMap 元素:

    1
    2
    3
    4
    5
    6
    7
    8
    
    <resultMap id="departmentResultMap" type="com.example.Department">
        <id property="id" column="department_id"/>
        <result property="name" column="department_name"/>
        <collection property="employees" ofType="com.example.Employee">
            <id property="id" column="employee_id"/>
            <result property="name" column="employee_name"/>
        </collection>
    </resultMap>
    
    • <collection> 元素:用于处理一对多关系,将多个对象(在此为 Employee)映射到 Department 对象的 employees 属性。
      • property 属性指定 Java 对象中的属性名。
      • ofType 属性指定集合中元素的 Java 类型。
  • select 语句:

    1
    2
    3
    4
    5
    6
    7
    
    <select id="getDepartmentById" resultMap="departmentResultMap">
        SELECT d.id as department_id, d.name as department_name,
               e.id as employee_id, e.name as employee_name
        FROM departments d
        LEFT JOIN employees e ON d.id = e.department_id
        WHERE d.id = #{id}
    </select>
    
    • 使用 LEFT JOIN 确保即使 Department 没有 Employee,也能正确映射。

使用 @Many 注解(Java 接口方式):

  • Java 类:

    1
    2
    3
    4
    5
    6
    7
    
    public class Department {
        private int id;
        private String name;
        @Many(select = "com.example.EmployeeMapper.getEmployeesByDepartmentId")
        private List<Employee> employees;
        // Getter 和 Setter 方法
    }
    
    • @Many 注解:
      • select 属性指定一个全限定的 SQL 映射语句的名称,该语句将返回关联对象的集合(在此为 Employee 列表)。

3. 多对多 (Many-to-Many) 关系映射

嵌套结果映射(XML 方式):

  • resultMap 元素:

    1
    2
    3
    4
    5
    6
    7
    8
    
    <resultMap id="studentResultMap" type="com.example.Student">
        <id property="id" column="student_id"/>
        <result property="name" column="student_name"/>
        <collection property="courses" ofType="com.example.Course">
            <id property="id" column="course_id"/>
            <result property="name" column="course_name"/>
        </collection>
    </resultMap>
    
    • 使用 collection 元素处理多对多关系,将多个 Course 对象映射到 Student 对象的 courses 属性。
  • select 语句:

    1
    2
    3
    4
    5
    6
    7
    8
    
    <select id="getStudentById" resultMap="studentResultMap">
        SELECT s.id as student_id, s.name as student_name,
               c.id as course_id, c.name as course_name
        FROM students s
        LEFT JOIN student_course sc ON s.id = sc.student_id
        LEFT JOIN courses c ON sc.course_id = c.id
        WHERE s.id = #{id}
    </select>
    
    • 使用多个 LEFT JOIN 处理多对多关系,通过中间表 student_course 关联 students 表和 courses 表。

4. 延迟加载

在 XML 中实现延迟加载:

  • 使用 fetchType 属性:

    1
    2
    3
    
    <association property="address" javaType="com.example.Address" fetchType="lazy">
        <!-- 映射信息 -->
    </association>
    
    • fetchType="lazy" 表示延迟加载,只有在访问关联对象时才会执行相应的 SQL 语句。

在 Java 接口中实现延迟加载:

  • 使用 @One@Many 注解:
    1
    2
    
    @One(select = "com.example.AddressMapper.getAddressById", fetch = FetchType.LAZY)
    private Address address;
    

5. 关联关系的维护

插入和更新操作:

  • 一对一和一对多关系:

    • 对于一对一和一对多关系,可以使用 insertupdate 语句更新关联对象。
    • 例如,在 Department 中添加 Employee 时,可以先插入 Employee,再更新 Department 的外键。
    1
    2
    3
    4
    5
    6
    7
    
    <insert id="insertEmployee">
        INSERT INTO employees (name, department_id) VALUES (#{name}, #{departmentId})
    </insert>
    
    <update id="updateDepartment">
        UPDATE departments SET name = #{name} WHERE id = #{id}
    </update>
    

多对多关系:

  • 使用中间表:

    • 插入操作需要同时插入 StudentCourse 和中间表的记录。
    • 更新操作可能涉及中间表的修改,例如添加或删除关联关系。
    1
    2
    3
    
    <insert id="insertStudentCourse">
        INSERT INTO student_course (student_id, course_id) VALUES (#{studentId}, #{courseId})
    </insert>
    

总结:

  • 结果映射:使用 resultMap 元素及其子元素(id, result, association, collection)将查询结果映射到 Java 对象,处理不同的关系类型。
  • 关联关系处理:使用 @One@Many 注解在 Java 类中指定关联关系和加载策略。
  • 延迟加载:通过 fetchType 属性或注解的 fetch 属性控制关联对象的加载时机,提高性能。
  • 关联关系维护:使用 insertupdate 语句处理对象的插入和更新,对于多对多关系,需特别注意中间表的操作。

注意事项:

  • 性能考虑

    • 过度使用立即加载可能导致性能问题,尤其是在复杂对象图中。
    • 延迟加载可能导致 LazyInitializationException,确保在适当的事务或会话范围内访问延迟加载的对象。
  • SQL 语句设计

    • 确保 SQL 语句正确使用 JOIN 操作符和正确的连接条件,以准确映射关联关系。
  • 映射维护

    • 对于复杂的关联关系,维护映射配置可能会变得复杂,确保映射的准确性和一致性。

通过以上详细的解释和示例,你可以在 MyBatis 中有效地实现和管理对象之间的各种关系映射,根据业务需求灵活选择不同的映射方式和加载策略,同时注意性能和数据一致性问题。