MyBatis 中的 <association> 和 <collection> 标签
在 MyBatis 中,<association> 和 <collection> 标签用于配置复杂查询的结果集关联映射,主要用于处理实体类之间的嵌套关系。二者的区别在于:
<association>标签用于一对一的关系映射,比如实体类 A 中包含一个实体类 B 的对象。<collection>标签用于一对多的关系映射,比如实体类 A 中包含一个 B 类对象的集合。
示例:<association> 和 <collection> 标签的区别
我们以下面的场景为例:
- 一个 User 实体类,它有一个 Address 实体类的属性(表示一对一关系)。
 - 一个 User 实体类,它有多个 Order 实体类的对象(表示一对多关系)。
 
数据库表结构
假设有三个表:user、address 和 orders,表结构如下:
user 表结构:
        
            
            
              sql
              
              
            
          
          CREATE TABLE user (
    id BIGINT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    address_id BIGINT
);
        address 表结构:
        
            
            
              sql
              
              
            
          
          CREATE TABLE address (
    id BIGINT PRIMARY KEY,
    city VARCHAR(50),
    street VARCHAR(100)
);
        orders 表结构:
        
            
            
              sql
              
              
            
          
          CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    product_name VARCHAR(100),
    price DECIMAL(10, 2)
);
        实体类定义
以下是 Java 实体类定义:
User 实体类:
            
            
              java
              
              
            
          
          public class User {
    private Long id;
    private String name;
    private int age;
    private Address address; // 一对一关系
    private List<Order> orders; // 一对多关系
    // getters and setters
}
        Address 实体类:
            
            
              java
              
              
            
          
          public class Address {
    private Long id;
    private String city;
    private String street;
    // getters and setters
}
        Order 实体类:
            
            
              java
              
              
            
          
          public class Order {
    private Long id;
    private Long userId;
    private String productName;
    private BigDecimal price;
    // getters and setters
}
        MyBatis 映射配置
UserMapper.xml 配置
在 UserMapper.xml 中使用 <association> 和 <collection> 标签来映射复杂关系:
            
            
              xml
              
              
            
          
          <mapper namespace="com.example.mapper.UserMapper">
    <!-- 查询用户及其地址和订单信息 -->
    <select id="getUserById" resultMap="userResultMap">
        SELECT
            u.id AS user_id, u.name, u.age,
            a.id AS address_id, a.city, a.street,
            o.id AS order_id, o.product_name, o.price
        FROM user u
        LEFT JOIN address a ON u.address_id = a.id
        LEFT JOIN orders o ON u.id = o.user_id
        WHERE u.id = #{id}
    </select>
    <!-- 用户结果映射 -->
    <resultMap id="userResultMap" type="com.example.entity.User">
        <id property="id" column="user_id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        
        <!-- 一对一映射 Address -->
        <association property="address" javaType="com.example.entity.Address" columnPrefix="address_" fetchType="lazy" select="com.example.mapper.AddressMapper.getAddressById">
            <id property="id" column="address_id"/>
            <result property="city" column="city"/>
            <result property="street" column="street"/>
        </association>
        <!-- 一对多映射 Orders -->
        <collection property="orders" ofType="com.example.entity.Order" columnPrefix="order_" fetchType="lazy">
            <id property="id" column="order_id"/>
            <result property="productName" column="product_name"/>
            <result property="price" column="price"/>
        </collection>
    </resultMap>
</mapper>
        - 
<association>标签 :用于映射 User 和 Address 之间的一对一关系,将 Address 的属性映射到 User 对象中的address属性上。columnPrefix属性:用于为关联对象的列指定前缀,以避免与其他表的列名冲突。fetchType属性 :用于指定加载方式,lazy表示懒加载,eager表示立即加载。select属性 :用于指定关联关系中需要调用的查询方法,这里可以指定一个 Mapper 方法(例如com.example.mapper.AddressMapper.getAddressById),以实现更灵活的关联查询。
 - 
<collection>标签 :用于映射 User 和 Order 之间的一对多关系,将 Order 的列表映射到 User 对象中的orders属性上。columnPrefix属性 :与<association>中的columnPrefix类似,用于避免列名冲突。fetchType属性:指定加载方式,支持懒加载和立即加载。
 
常用的高级属性与用法
除了上面提到的 <association> 和 <collection> 标签中使用的常见属性外,还有一些高级的标签和属性可以提高 MyBatis 的映射功能和灵活性:
- 
resultMap标签中的autoMapping属性:autoMapping:自动映射列到属性,true表示自动进行映射,false表示不自动映射,可以用于减少手动配置的工作量。
xml<resultMap id="userResultMap" type="com.example.entity.User" autoMapping="true"> ... </resultMap> - 
<constructor>标签:<constructor>标签用于在对象创建时通过构造函数传递参数。- 可以使用 
<idArg>和<arg>标签来指定构造函数参数的映射关系。 
xml<resultMap id="userResultMap" type="com.example.entity.User"> <constructor> <idArg column="user_id" javaType="Long"/> <arg column="name" javaType="String"/> <arg column="age" javaType="int"/> </constructor> ... </resultMap> - 
<discriminator>标签:- 用于处理多态对象的映射关系。可以根据某个字段的值来动态选择不同的子类映射。
 - 例如根据用户类型字段 
user_type来区分普通用户和管理员。 
xml<resultMap id="userResultMap" type="com.example.entity.User"> <id property="id" column="user_id"/> <result property="name" column="name"/> <discriminator javaType="String" column="user_type"> <case value="ADMIN" resultType="com.example.entity.AdminUser"/> <case value="USER" resultType="com.example.entity.RegularUser"/> </discriminator> </resultMap> - 
<collection>和<association>的cascade属性:cascade:指定在父对象操作时是否对关联对象进行级联操作,例如插入、更新和删除。MyBatis 不直接提供cascade的配置,但可以通过编程方式实现类似的级联操作。
 - 
select属性的动态关联:select属性 可以使用动态参数,灵活地调用其他查询,特别是在需要根据不同条件关联查询的时候,能极大增强查询能力。
 
Mapper 接口
定义 UserMapper 接口用于数据库访问:
            
            
              java
              
              
            
          
          public interface UserMapper {
    User getUserById(Long id);
}
        定义 AddressMapper 接口,用于单独查询 Address 数据:
            
            
              java
              
              
            
          
          public interface AddressMapper {
    Address getAddressById(Long id);
}
        Service 层
在 Service 层通过注入 UserMapper 来调用查询方法:
            
            
              java
              
              
            
          
          @Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public User getUserById(Long id) {
        return userMapper.getUserById(id);
    }
}
        Controller 层
在 Controller 层处理前端的请求,返回用户数据:
            
            
              java
              
              
            
          
          @RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }
}
        前端返回的 JSON 数据
假设用户的 ID 为 1,数据库中的数据如下:
User表中:{id: 1, name: "John", age: 30, address_id: 1}Address表中:{id: 1, city: "New York", street: "5th Avenue"}Orders表中有两条记录:{id: 1, user_id: 1, product_name: "Laptop", price: 1200.00}{id: 2, user_id: 1, product_name: "Smartphone", price: 800.00}
返回的 JSON 数据将会是:
            
            
              json
              
              
            
          
          {
  "id": 1,
  "name": "John",
  "age": 30,
  "address": {
    "id": 1,
    "city": "New York",
    "street": "5th Avenue"
  },
  "orders": [
    {
      "id": 1,
      "productName": "Laptop",
      "price": 1200.00
    },
    {
      "id": 2,
      "productName": "Smartphone",
      "price": 800.00
    }
  ]
}
        总结
<association>标签 用于一对一映射,将Address映射到User对象的address属性中,支持属性如columnPrefix、fetchType和select来控制列前缀、加载方式和关联查询。<collection>标签 用于一对多映射,将Order列表映射到User对象的orders属性中,类似地支持columnPrefix和fetchType等属性。<constructor>标签 可以用于通过构造函数创建对象。<discriminator>标签 适用于多态映射,根据某些字段动态选择子类映射。
这些高级标签和属性在 MyBatis 的复杂查询和结果集映射中非常有用,能有效地将关系型数据库的数据转换为 Java 对象,从而简化开发工作。