<collection> 和 <association>的详细用法(附详细代码解析)

MyBatis 中的 <association><collection> 标签

在 MyBatis 中,<association><collection> 标签用于配置复杂查询的结果集关联映射,主要用于处理实体类之间的嵌套关系。二者的区别在于:

  • <association> 标签用于一对一的关系映射,比如实体类 A 中包含一个实体类 B 的对象。
  • <collection> 标签用于一对多的关系映射,比如实体类 A 中包含一个 B 类对象的集合。

示例:<association><collection> 标签的区别

我们以下面的场景为例:

  • 一个 User 实体类,它有一个 Address 实体类的属性(表示一对一关系)。
  • 一个 User 实体类,它有多个 Order 实体类的对象(表示一对多关系)。

数据库表结构

假设有三个表:useraddressorders,表结构如下:

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 的映射功能和灵活性:

  1. resultMap 标签中的 autoMapping 属性

    • autoMapping :自动映射列到属性,true 表示自动进行映射,false 表示不自动映射,可以用于减少手动配置的工作量。
    xml 复制代码
    <resultMap id="userResultMap" type="com.example.entity.User" autoMapping="true">
        ...
    </resultMap>
  2. <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>
  3. <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>
  4. <collection><association>cascade 属性

    • cascade :指定在父对象操作时是否对关联对象进行级联操作,例如插入、更新和删除。MyBatis 不直接提供 cascade 的配置,但可以通过编程方式实现类似的级联操作。
  5. 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 属性中,支持属性如 columnPrefixfetchTypeselect 来控制列前缀、加载方式和关联查询。
  • <collection> 标签 用于一对多映射,将 Order 列表映射到 User 对象的 orders 属性中,类似地支持 columnPrefixfetchType 等属性。
  • <constructor> 标签 可以用于通过构造函数创建对象。
  • <discriminator> 标签 适用于多态映射,根据某些字段动态选择子类映射。

这些高级标签和属性在 MyBatis 的复杂查询和结果集映射中非常有用,能有效地将关系型数据库的数据转换为 Java 对象,从而简化开发工作。

相关推荐
希忘auto24 分钟前
详解Redis的常用命令
redis·1024程序员节
云边有个稻草人28 分钟前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法
半盏茶香29 分钟前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
旭久31 分钟前
SpringBoot的Thymeleaf做一个可自定义合并td的pdf表格
pdf·html·springboot
Evand J1 小时前
LOS/NLOS环境建模与三维TOA定位,MATLAB仿真程序,可自定义锚点数量和轨迹点长度
开发语言·matlab
LucianaiB1 小时前
探索CSDN博客数据:使用Python爬虫技术
开发语言·爬虫·python
Ronin3051 小时前
11.vector的介绍及模拟实现
开发语言·c++
计算机学长大白2 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言
suweijie7683 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿3 小时前
List深拷贝后,数据还是被串改
java