当前位置: 首页 > news >正文

MyBatis-Flex关联查询

MyBatis-Flex关联查询

在 MyBatis-Flex 中,我们内置了 3 种方案,帮助用户进行关联查询,比如 一对多一对一多对一多对多等场景,他们分别是:

  • 方案1:Relations 注解
  • 方案2:Field Query
  • 方案3:Join Query

其中理解一下使用注解形式关联查询

方案一:

在 MyBatis-Flex 中,提供了 4 个 Relations 注解,他们分别是:

  • RelationOneToOne:用于一对一的场景
  • RelationOneToMany:用于一对多的场景
  • RelationManyToOne:用于多对一的场景
  • RelationManyToMany:用于多对多的场景

一对一 @RelationOneToOne

1、什么是:RelationOneToOne

它是用来告诉 MyBatis-Flex:这两个对象之间是一对一的关系,查询账户的时候,顺便把对应的身份证也查出来。

你可以把「账户」和「身份证」看作现实生活中的「一个人」和「他的身份证」,一个人只能有一个身份证,这就是所谓的 一对一关系。代码如下所示:

Account.java :

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;

    private String userName;

    @RelationOneToOne(selfField = "id", targetField = "accountId")
    private IDCard idCard;

    //getter setter
}

这个意思是:

  • 我的 Account(账户)类里面有一个 idCard(身份证)属性。
  • 我要把 idIDCard 中的 accountId 对上号,才能找到它的身份证。

你可以理解成「Account 的 id 是 1,就去 IDCard 表里找 accountId=1 的那张身份证。」

IDCard.java :

@Table(value = "tb_idcard")
public class IDCard implements Serializable {

    private Long accountId;
    private String cardNo;
    private String content;

    //getter setter
}

这个方法跟普通的 selectAll() 不一样,它会自动把关联的 IDCard 一起查出来。

如果你写的是:

accountMapper.selectAll(); // 这个不会查身份证,idCard 是 null

那就查不到身份证信息。

2、 查询的 SQL 是啥样的?

它其实会执行两条 SQL:

-- 查账户
SELECT id, user_name FROM tb_account;

-- 再查身份证
SELECT account_id, card_no, content FROM tb_idcard WHERE account_id IN (1,2,3,4,5);

也就是说,它帮你查了两次,然后把数据合并起来了。对应的结果如下:

[
 Account{id=1, userName='孙悟空', idCard=IDCard{accountId=1, cardNo='0001', content='内容1'}},
 Account{id=2, userName='猪八戒', idCard=IDCard{accountId=2, cardNo='0002', content='内容2'}},
 ...
]
补充说明
情况 1:IDCard 是 VO 或 DTO

如果你这个 IDCard 不是数据库实体(也就是没有 @Table 注解),比如只是个数据传输对象,那你得手动告诉它「去哪个表查」。

@RelationOneToOne(targetField = "accountId", targetTable = "tb_idcard")
总结一句话:

@RelationOneToOne 可以让你在查主表的时候,自动把关联的子表(比如身份证)一块查出来,少写 SQL,多做事

一对多关系@RelationOneToMany

假设你有一个账户(Account),这个账户买了很多本书(Book)。一个账户对应很多本书,这就是典型的一对多关系。代码如下:

public class Account {
    private Long id;
    private String userName;

    @RelationOneToMany(selfField = "id", targetField = "accountId")
    private List<Book> books; // 这个账户拥有的书籍列表
}
  • selfField = "id":账户自己的 id
  • targetField = "accountId":书籍表中表示“属于哪个账户”的字段

就是:我账户的 id = 1,就去 Book 表里查 accountId = 1 的所有书,执行的 SQL:

SELECT id, user_name FROM tb_account;

SELECT id, account_id, title FROM tb_book WHERE account_id IN (1, 2, 3, 4, 5);

它会自动查出每个账户的所有书,帮你组装好 List<Book>。每个账户对象里,books 属性都是一个 List。查询结果如下:

Account{id=1, userName="孙悟空", books=[
    Book{id=1, accountId=1, title="西游记"},
    Book{id=2, accountId=1, title="大圣归来"}
]}
进阶用法:
Map 映射想把书存在 Map 里咋办?

复制编辑
@RelationOneToMany(selfField = "id", targetField = "accountId", mapKeyField = "id")
private Map<Long, Book> books;
  • 意思是:把每本书的 id 当作 Map 的 key,Book 当作 value 存起来
{
 1L -> Book{id=1, accountId=1, title="西游记"},
 2L -> Book{id=2, accountId=1, title="大圣归来"}
}

是不是很方便用 key 快速访问某本书?

selfValueSplitBy 分割查询

场景举例:

病人表 Patient 里有个字段 diseaseIds 是个字符串:
比如 "1,2,3" 表示这个人有 1、2、3 号疾病。

我们希望在查出病人的时候,自动把这些疾病的名字查出来,甚至生成 List<String>Map<String, Disease>


@RelationOneToMany(
    selfField = "diseaseIds",         // 是个字符串,像 "1,2,3"
    selfValueSplitBy = ",",           // 用逗号分割
    targetTable = "tb_disease",       // 从 tb_disease 表中查
    targetField = "diseaseId",        // 对应疾病表的主键
    valueField = "name"               // 我只要疾病名就行
)
private List<String> diseaseNameList;

还可以这样:

@RelationOneToMany(
    selfField = "diseaseIds",
    selfValueSplitBy = ",",
    targetField = "diseaseId",
    mapKeyField = "diseaseId"
)
private Map<String, Disease> diseaseMap;

执行的 SQL

-- 查病人
SELECT patient_id, name, disease_ids, tag_ids FROM tb_patient;

-- 再查疾病信息
SELECT disease_id, name FROM tb_disease WHERE disease_id IN ('1','2','3');

查询结果

{
  "patientId": 4,
  "name": "赵六",
  "diseaseIds": "1,2,3",
  "diseaseNameList": [
    "心脑血管疾病",
    "消化系统疾病",
    "神经系统疾病"
  ],
  "diseaseMap": {
    "1": {"diseaseId": "1", "name": "心脑血管疾病"},
    "2": {"diseaseId": "2", "name": "消化系统疾病"},
    "3": {"diseaseId": "3", "name": "神经系统疾病"}
  }
}

是不是很神奇?你只存了个 "1,2,3" 的字符串,它就帮你拆开、查表、拼好返回。总结一句话:

  • @RelationOneToMany:一对多,查主表时自动加载子表列表(List/Map 都行);
  • mapKeyField:可以让子表结果以 Map 的形式返回;
  • selfValueSplitBy:把主表字段按逗号、斜杠等切割成多个值,查子表用;
  • valueField:只要子表中的某个字段,直接返回成 List,比如 List。

多对一 @RelationManyToOne

场景举例

  • 一个账户(Account)可以拥有很多本书(Book)——这是“一对多”;
  • 从书籍(Book)的角度来看,每本书只属于一个账户——这就是“多对一”。

所以现在我们站在 Book(多) 的角度,去查它所属的 Account(一),就是“多对一”。

账户类(Account)——还是很简单的主表:

public class Account implements Serializable {
    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    // getter / setter
}

书籍类(Book)——配置多对一关系:

@Table(value = "tb_book")
public class Book implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private Long accountId; // 外键,关联账户
    private String title;

    @RelationManyToOne(selfField = "accountId", targetField = "id")
    private Account account; // 多对一:一本书关联一个账户

    // getter / setter
}

注解说明
  • selfField = "accountId":表示书籍里这个字段是“关联账户的 ID”;
  • targetField = "id":表示账户类的主键;
  • 因为账户类的主键是 id,所以可以简写为:
@RelationManyToOne(selfField = "accountId")
查询方式
List<Book> books = bookMapper.selectAllWithRelations();

注意: selectAllWithRelations() 必须使用这个方法,MyBatis-Flex 才会自动加载关联关系。

SQL 实际执行
-- 查书本
SELECT id, account_id, title FROM tb_book;

-- 查账户
SELECT id, user_name FROM tb_account WHERE id IN (1, 2, 3);

系统会自动分析你有哪些 accountId,一次性查出相关账户信息,组装进去。

查询结果展示
Book{id=1, title="Java 编程", accountId=1, account={
    id=1, userName="孙悟空"
}}

Book{id=2, title="Spring Boot", accountId=2, account={
    id=2, userName="猪八戒"
}}

你只写了一个 accountId,系统就帮你查到了完整的 Account 对象。


总结一句话:

@RelationManyToOne 是从“子表”反向查“主表”的配置方式,常见于像“书籍 -> 账户”,“订单 -> 用户”,“评论 -> 帖子”这样的场景。

多对多 @RelationManyToMany

场景举例

我们来看一个经典的业务场景:

一个账户(Account)可以拥有多个角色(Role),比如“管理员”、“用户”、“VIP”;

一个角色(Role)也可以被多个账户拥有,比如“管理员”可能对应了孙悟空、猪八戒两个账户。

这个就是一个标准的 多对多关系


多对多实现过程?

多对多在数据库中,不能直接一张表对另一张表,所以需要用一张 中间表 来“桥接”两边的关系。

中间表叫做:tb_role_mapping,结构如下:

CREATE TABLE tb_role_mapping (
  account_id INTEGER,
  role_id INTEGER
);

作用:
记录“哪个账户”关联了“哪个角色”。

比如:

account_idrole_id
11
12
21

这表示:

  • 账户1有角色1和2
  • 账户2有角色1

实体类写法

Account.java

public class Account implements Serializable {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;

    @RelationManyToMany(
        joinTable = "tb_role_mapping",         // 中间表
        joinSelfColumn = "account_id",         // 中间表中对 account 的字段
        joinTargetColumn = "role_id"           // 中间表中对 role 的字段
    )
    private List<Role> roles;                  // 多个角色

    // getter / setter
}

Role.java

@Table(value = "tb_role")
public class Role implements Serializable {

    private Long id;
    private String name;

    // getter / setter
}
注解说明
属性说明
joinTable中间表的名字(桥梁)
joinSelfColumn中间表里对应当前类(Account)的字段
joinTargetColumn中间表里对应目标类(Role)的字段
selfFieldtargetField当前类 和 目标类 的主键字段名,如果默认是 id,可以省略

所以只写:

@RelationManyToMany(
  joinTable = "tb_role_mapping",
  joinSelfColumn = "account_id",
  joinTargetColumn = "role_id"
)

就够了,MyBatis-Flex 会自动去找 id 字段。

查询示例
List<Account> accounts = accountMapper.selectAllWithRelations();

执行的 SQL 类似于:

-- 查账户
SELECT id, user_name FROM tb_account;

-- 查中间表
SELECT account_id, role_id FROM tb_role_mapping WHERE account_id IN (1, 2, 3);

-- 查角色表
SELECT id, name FROM tb_role WHERE id IN (1, 2, 3);
查询结果
[
  Account{
    id=1,
    userName='孙悟空',
    roles=[
      Role{id=1, name='管理员'},
      Role{id=2, name='VIP'}
    ]
  },
  Account{
    id=2,
    userName='猪八戒',
    roles=[
      Role{id=1, name='管理员'}
    ]
  }
]

MyBatis-Flex 会自动:

  1. 查出账户;
  2. 用中间表找到账户对应的角色ID;
  3. 再查出对应的角色列表,自动装配进来。
总结一句话:

@RelationManyToMany 就是用来处理“多对多”的关系,通过一个中间表桥接两个实体,MyBatis-Flex 自动帮你搞定 SQL 和关系映射。

相关文章:

  • 数字内容体验案例分析的核心指标是什么?
  • 【学Rust写CAD】33 近似 Alpha 混合函数(argb.rs补充方法)
  • 人大金仓数据库dum文件进行备份数据和恢复数据
  • 微前端随笔
  • 架构思维:限流技术深度解析
  • 20250406华南金牌的X99主板突然不能上网的处理流程【RTL8211不稳定】
  • JavaScript Math(算数)
  • 生成式人工智能(AIGC):内容创作的新引擎与新挑战
  • CompletableFuture:整合、超时、完成事件与批量处理
  • 处理甘特图启动依赖报错。
  • vite.config.js常用配置
  • STM32_USB
  • 提升Spring Boot开发效率的Idea插件:Spring Boot Helper
  • 【LLM】DeepResearch系列(Search-R1、Search-o1、R1-Searcher)
  • 大模型最新面试题系列:模型部署(二)
  • io_uring 异步 socket 编程
  • 自动化框架及其设计搭建浅谈(二)--分层自动化测试
  • 浮点数精度问题
  • vue项目中,添加主题皮肤切换功能
  • 负指数二项式展开
  • 网页案例/成都搜索优化排名公司
  • 文章类型网站/郑州seo哪家好
  • 网站关键词设置几个/湖南网站seo地址
  • 自建网站支付问题/苹果要做搜索引擎
  • 的网站建设公司哪家好/深圳企业网站制作公司
  • 茌平网站建设公司/互联网营销有哪些方式