MyBatis 一对一查询中的 `<association>` 标签配置详解
MyBatis 一对一查询中的 <association> 标签配置详解
引言
在使用 MyBatis 进行数据库操作时,一对一查询是一种常见的需求。尤其是在处理主表和从表之间的关联关系时,如何正确配置 <association> 标签成为了一个关键问题。本文将通过一个具体的案例,详细分析 MyBatis 中 <association> 标签的配置方式,并解决在实际开发中遇到的用户编号不一致的问题。
1. 案例背景
我们有两个表:
- Order表(主表):存储订单信息,包含字段- id(订单ID)、- user_id(用户ID)、- order_number(订单编号)。
- User表(从表):存储用户信息,包含字段- id(用户ID)、- user_name(用户名)、- password(密码)、- name(姓名)、- age(年龄)、- sex(性别)。
表关系如下

需求是通过订单编号查询订单信息,并同时查询出下单用户的信息。
2. 实体类定义
- 
Order实体类:public class Order { private Integer id; // 订单ID private Long userID; // 用户ID private String orderNumber; // 订单编号 private User user; // 关联的用户对象 // 省略 getter 和 setter 方法 }
- 
User实体类:public class User { private Long id; // 用户ID private String userName; // 用户名 private String passWord; // 密码 private String name; // 姓名 private Integer age; // 年龄 private Integer sex; // 性别 // 省略 getter 和 setter 方法 }
3. SQL 查询
为了实现一对一查询,我们使用 JOIN 语句将 Order 表和 User 表关联起来:
SELECT 
    o.id AS order_id, 
    o.user_id AS order_user_id, 
    o.order_number AS order_number, 
    u.id AS user_id, 
    u.user_name AS user_name, 
    u.password AS password, 
    u.name AS name, 
    u.age AS age, 
    u.sex AS sex 
FROM 
    tb_order o 
INNER JOIN 
    tb_user u 
ON 
    o.user_id = u.id 
WHERE 
    o.order_number = #{orderNumber}

4. MyBatis 映射文件
在 MyBatis 中,使用 <resultMap> 和 <association> 标签来配置一对一映射关系。<association>标签里面 写的是添加从表中字段与实体属性映射关系
(1)错误配置
以下是错误的 <resultMap> 配置:
<resultMap id="findOrderByOrderNumberResultMap" type="Order" autoMapping="true">
    <!-- 主表(Order)的映射 -->
    <id column="order_id" property="id"/> <!-- Order 表的主键 -->
    <result column="user_id" property="userID"/> <!-- Order 表的外键 -->
    <result column="order_number" property="orderNumber"/> <!-- Order 表的其他字段 -->
    <!-- 从表(User)的映射 -->
    <association property="user" javaType="User" autoMapping="true">
        <id column="id" property="id"/> <!-- 错误的配置:将 Order 表的 id 列映射到 User 实体的 id 属性 -->
        <result column="user_name" property="userName"/>
        <result column="password" property="passWord"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="sex" property="sex"/>
    </association>
</resultMap>
(2)错误原因分析
- column="id":这里将- Order表中的- id列映射到- User实体类中的- id属性。
- 问题:Order表中的id是订单的主键,而User表中的id是用户的主键。这两个字段没有直接关系,因此会导致映射错误。
- 结果:User实体类中的id属性被错误地赋值为Order表中的id,而不是User表中的id。

(3)正确配置
以下是正确的 <resultMap> 配置:
<resultMap id="findOrderByOrderNumberResultMap" type="Order" autoMapping="true">
    <!-- 主表(Order)的映射 -->
    <id column="order_id" property="id"/> <!-- Order 表的主键 -->
    <result column="user_id" property="userID"/> <!-- Order 表的外键 -->
    <result column="order_number" property="orderNumber"/> <!-- Order 表的其他字段 -->
    <!-- 从表(User)的映射 -->
    <association property="user" javaType="User" autoMapping="true">
        <id column="user_id" property="id"/> <!-- 正确的配置:将 User 表的 id 列映射到 User 实体的 id 属性 -->
        <result column="user_name" property="userName"/>
        <result column="password" property="passWord"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="sex" property="sex"/>
    </association>
</resultMap>
(4)正确配置分析
- column="user_id":这里将- Order表中的- user_id列映射到- User实体类中的- id属性。
- 原因:Order表中的user_id是外键,指向User表中的id。通过这个外键字段,MyBatis 可以正确查询到User表中的对应记录。
- 结果:User实体类中的id属性被正确地赋值为User表中的id。

5. 测试与结果
(1)错误配置的测试结果
通过错误配置查询订单编号为 20140921003 的订单信息,并输出结果:
@Test
public void testFindOrderByOrderNumber() {
    Order order = orderMapper.findOrderByOrderNumber("20140921003");
    System.out.println(order);
}
输出结果:
Order{id=3, userID=1, orderNumber='20140921003', user=User{id=3, userName='zhangsan', passWord='123456', name='张三', age=30, sex=1}}
- 问题:User实体类中的id属性被错误地赋值为Order表中的id(3),而不是User表中的id(1)。
(2)正确配置的测试结果
通过正确配置查询订单编号为 20140921003 的订单信息,并输出结果:
@Test
public void testFindOrderByOrderNumber() {
    Order order = orderMapper.findOrderByOrderNumber("20140921003");
    System.out.println(order);
}
输出结果:
Order{id=3, userID=1, orderNumber='20140921003', user=User{id=1, userName='zhangsan', passWord='123456', name='张三', age=30, sex=1}}
- 结果:User实体类中的id属性被正确地赋值为User表中的id(1)。
6. 核心问题
在 MyBatis 的一对一查询中,<association> 标签的配置是关键。尤其是 column 和 property 的配置,直接影响查询结果的正确性。
- column:应该填写 主表的外键字段(例如- tb_order表中的- user_id),而不是从表的主键字段(例如- tb_user表中的- id)。
- property:应该填写 从表实体类中的属性名(例如- User实体类中的- id)。
7. 错误配置分析
以下是一个错误的配置示例:
<association property="user" javaType="User">
    <id column="id" property="id"/>
</association>
- 错误原因:
- column="id":这里将- Order表中的- id列映射到- User实体类中的- id属性。
- 问题:Order表中的id是订单的主键,而User表中的id是用户的主键。这两个字段没有直接关系,因此会导致映射错误。
- 结果:User实体类中的id属性被错误地赋值为Order表中的id,而不是User表中的id。
 
8. 正确配置分析
以下是正确的配置示例:
<association property="user" javaType="User">
    <id column="user_id" property="id"/>
</association>
- 正确原因:
- column="user_id":这里将- Order表中的- user_id列映射到- User实体类中的- id属性。
- 原因:Order表中的user_id是外键,指向User表中的id。通过这个外键字段,MyBatis 可以正确查询到User表中的对应记录。
- 结果:User实体类中的id属性被正确地赋值为User表中的id。
 
9. 映射规则总结
- 主表的外键字段 (column):用来指示主表和从表的关联。一般来说,主表中的外键字段会指向从表中的主键字段。
- 从表的主键字段(通常是 id):在 POJO 类中对应的属性(property)会映射到主表查询结果的某个字段。
10. 为什么 column="user_id" 是正确的?
- 主表外键字段:在 tb_order表中,user_id是外键,它指向tb_user表的id字段。为了通过tb_order表来查询相关的User信息,必须通过user_id来建立和tb_user表的关系。
- 映射关系:在 <association>标签中,column="user_id"就是将主表的外键字段user_id与从表(tb_user)的主键字段id关联起来,然后 MyBatis 会使用这个外键字段去查询从表tb_user中的对应记录。
11. 错误场景分析
如果像这样设置:
<association property="user" javaType="User">
    <id column="id" property="id"/>
</association>
- column="id":这时 MyBatis 会将- tb_order表中的- id字段(订单的主键)与从表- tb_user表中的- id字段进行匹配。显然,这个逻辑是错的,因为- tb_order表中的- id字段和- tb_user表的- id字段没有直接关系,- tb_order的- id只是订单的主键,而- user_id才是关联用户表的外键字段。
12.怎么理解<association> 标签的主要作用是配置主表实体类与从表实体类之间的关系
在这个标签中,我们通过指定从表的字段与实体类的属性之间的映射关系,将从表的查询结果正确地注入到主表实体类中。
简单来说,<association> 标签的功能是告诉 MyBatis:从表的查询结果要如何映射到主表实体类中的某个属性。
核心概念理解
- 
主表和从表的关系: - 主表(如 Order):在查询中是主要表,实体类中包含一个从表对象(如User)。
- 从表(如 User):作为关联表,其字段会被映射到主表实体类中对应的属性。
 
- 主表(如 
- 
property:- 表示主表实体类中的属性名(主表实体类中的一个字段)。
- 这个属性通常是从表实体类的对象,比如 Order中的User user。
 
- 
column:- 表示从表的字段或与主表的外键字段相关联的字段。
 
- 
<association>的具体作用:- 把从表的字段值(数据库的列值)映射到主表实体类中对应的属性(property)。
- 通过 column指定主表外键字段,将其用于从表的查询。
 
- 把从表的字段值(数据库的列值)映射到主表实体类中对应的属性(
关键配置
在 <association> 标签内部,配置从表的字段与从表实体类属性的对应关系。具体包括以下两点:
1. 从表字段映射
通过 <result> 和 <id> 标签,定义从表字段到从表实体属性的映射,例如:
<association property="user" javaType="User">
    <id column="user_id" property="id"/> <!-- 主表的 user_id 外键映射到从表 User 的 id 属性 -->
    <result column="user_name" property="userName"/> <!-- 从表的 user_name 映射到 User 类的 userName 属性 -->
    <result column="password" property="passWord"/> <!-- 从表的 password 映射到 User 类的 passWord 属性 -->
    <result column="name" property="name"/> <!-- 从表的 name 映射到 User 类的 name 属性 -->
    <result column="age" property="age"/> <!-- 从表的 age 映射到 User 类的 age 属性 -->
    <result column="sex" property="sex"/> <!-- 从表的 sex 映射到 User 类的 sex 属性 -->
</association>
2. 主表外键与从表主键的映射
column 中填写主表的外键字段,property 对应的是从表实体类的主键字段。例如:
<id column="user_id" property="id"/>
- column="user_id":主表- Order的外键字段- user_id。
- property="id":从表实体类- User中的主键字段- id。
MyBatis 会利用 user_id 去从表中查询对应的用户信息,然后将结果注入到主表实体类的 user 属性中。
如何理解“添加从表字段与实体属性的映射关系”
在 <association> 中添加从表字段与实体属性的映射关系,简单来说就是以下过程:
- 
从表字段和实体属性的映射: - 数据库中的从表字段(列名)会被映射到从表实体类的某个属性上。
- 例如:
- 数据库字段 user_name-> 从表实体类User的userName属性。
- 数据库字段 password-> 从表实体类User的passWord属性。
 
- 数据库字段 
 
- 
从表实体映射到主表属性: - 主表实体类的某个属性(如 Order的User user)需要映射从表的整个实体类。
- MyBatis 会通过主表的外键字段(如 user_id)与从表的主键字段(如id)建立关联,将从表的实体对象注入到主表。
 
- 主表实体类的某个属性(如 
简单类比:拼图原理
把 <association> 标签理解为拼图工具:
- 数据库中的从表字段是拼图碎片。
- 从表实体类的属性是拼图目标。
- <association>的配置就是在定义:哪些碎片对应拼成目标中的哪个部分。
最终,这些“拼好”的从表数据会被作为一个完整对象(从表实体类)注入到主表实体类中。
13 总结
- 
<association>标签内部的字段映射(从表字段 -> 从表实体类属性)用于:- 把从表的查询结果映射到从表实体类的属性中。
- 再将从表实体类整体注入到主表实体类的某个属性中。
 
- 
配置关键: - 明确主表外键字段和从表主键字段的对应关系(column和property)。
- 正确配置从表字段与从表实体类属性的映射关系。
 
- 明确主表外键字段和从表主键字段的对应关系(
通过正确的配置,可以实现主表与从表实体类之间的数据注入,满足实际开发中的一对一查询需求。
- 
column:应该填写 主表的外键字段(例如tb_order表中的user_id)。
- 
property:应该填写 从表实体类中的属性名(例如User实体类中的id)。
- 
映射逻辑:MyBatis 会通过主表的外键字段( user_id)去查询从表(tb_user)中的对应记录,并将结果映射到从表实体类中。
- 
column和property的区别:- column是数据库表中的列名。
- property是实体类中的属性名。
 
- 
<association>标签的作用:用于配置主表实体与从表实体之间的一对一关系。
- 
<id column="user_id" property="id"/>的含义:将查询结果集中的user_id列映射到User实体类中的id属性。
- 
数据一致性:确保数据库中的外键( Order.user_id)与主键(User.id)一致,避免映射错误。
通过以上步骤,我们成功实现了一对一查询,并解决了映射中的疑问。希望这篇博客能帮助你更好地理解 MyBatis 的一对一查询机制,并在实际开发中避免类似的问题。
作者:周政然
日期:2024年1月4日
版权声明:本文为原创文章,转载请注明出处。
希望这篇博客对你有所帮助!如果还有疑问,欢迎继续讨论!
 
                    
                     
                    
                 
                    
                 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号