• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

奋斗的软件工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

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(性别)。

表关系如下

image

需求是通过订单编号查询订单信息,并同时查询出下单用户的信息。


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}

image


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。

image

(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。

image


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:从表的查询结果要如何映射到主表实体类中的某个属性。


核心概念理解

  1. 主表和从表的关系:

    • 主表(如 Order):在查询中是主要表,实体类中包含一个从表对象(如 User)。
    • 从表(如 User):作为关联表,其字段会被映射到主表实体类中对应的属性。
  2. property:

    • 表示主表实体类中的属性名(主表实体类中的一个字段)。
    • 这个属性通常是从表实体类的对象,比如 Order 中的 User user。
  3. column:

    • 表示从表的字段或与主表的外键字段相关联的字段。
  4. <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> 中添加从表字段与实体属性的映射关系,简单来说就是以下过程:

  1. 从表字段和实体属性的映射:

    • 数据库中的从表字段(列名)会被映射到从表实体类的某个属性上。
    • 例如:
      • 数据库字段 user_name -> 从表实体类 User 的 userName 属性。
      • 数据库字段 password -> 从表实体类 User 的 passWord 属性。
  2. 从表实体映射到主表属性:

    • 主表实体类的某个属性(如 Order 的 User user)需要映射从表的整个实体类。
    • MyBatis 会通过主表的外键字段(如 user_id)与从表的主键字段(如 id)建立关联,将从表的实体对象注入到主表。

简单类比:拼图原理

把 <association> 标签理解为拼图工具:

  • 数据库中的从表字段是拼图碎片。
  • 从表实体类的属性是拼图目标。
  • <association> 的配置就是在定义:哪些碎片对应拼成目标中的哪个部分。

最终,这些“拼好”的从表数据会被作为一个完整对象(从表实体类)注入到主表实体类中。


13 总结

  • <association> 标签内部的字段映射(从表字段 -> 从表实体类属性)用于:

    1. 把从表的查询结果映射到从表实体类的属性中。
    2. 再将从表实体类整体注入到主表实体类的某个属性中。
  • 配置关键:

    • 明确主表外键字段和从表主键字段的对应关系(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日
版权声明:本文为原创文章,转载请注明出处。


希望这篇博客对你有所帮助!如果还有疑问,欢迎继续讨论!

posted on 2025-01-05 19:53  周政然  阅读(345)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3