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号