MyBatis-Flex多表关联查询指南分享

 更新时间:2026年05月15日 10:38:30   作者:友莘居士  
本文介绍了MyBatis-Flex的多表关联查询功能,包括基于QueryWrapper的动态关联查询和基于@Relation注解的静态关联查询,通过示例展示了如何实现不同类型的关联关系,并对比了两种方式的特点和适用场景,最后给出了最佳实践建议

MyBatis-Flex 提供了强大且灵活的多表关联查询支持,其核心是通过 QueryWrapperRelations 注解来实现的。与 MyBatis-Plus 等框架的关联查询不同,MyBatis-Flex 的关联查询是在不涉及 JOIN 的 SQL 的情况下,通过多次查询来实现的,这种方式在某些场景下(如分页、大数据量)反而有更好的性能表现。

MyBatis-Flex 的多表关联主要分为两种类型:

  1. 基于 QueryWrapper 的关联查询(动态、手动)
  2. 基于 @Relation 注解的关联查询(静态、自动)

1. 基于QueryWrapper的关联查询 (Manual)

这种方式非常灵活,你可以通过 QueryWrapper 动态地指定关联关系和查询条件。它主要通过 select(...)leftJoin/innerJoin 等方法来实现,但其底层机制依然是分开查询再组合。

核心语法示例:

假设我们有 Account(账户)表和 Book(书籍)表,一个账户对应多本书。

// 1. 查询账户及其书籍(1对多)
QueryWrapper query = QueryWrapper.create()
    .select() // 查询主表(Account)的所有字段
    .from(Account.class)
    // 使用 leftJoin 或 innerJoin 定义关联关系(注意:这只是定义关系,不生成SQL JOIN)
    .leftJoin(Book.class).on(Account::getId).eq(Book::getAccountId))
    // 添加查询条件
    .where(Account::getUserName).like("张三")
    .and(Book::getPublishDate).ge(LocalDate.now().minusYears(1));

// 执行查询
// 注意:这里返回的仍然是 List<Account>,但MyBatis-Flex会通过额外查询自动填充其Book列表
List<Account> accounts = accountMapper.selectListByQuery(query);

关键点:

  • leftJoin(Book.class)on(...) 方法定义了 AccountBook 之间的关联关系。

MyBatis-Flex 看到这个定义后,会执行以下步骤:

  1. 首先查询满足条件的 Account 列表。
  2. 然后根据 on 条件中定义的关联关系(Account.id = Book.account_id),再发起一次查询,获取所有相关的 Book
  3. 最后,自动将 Book 列表设置到对应 Account 对象的 books 属性中(需要你有这个属性)。

2. 基于@Relation注解的关联查询 (Auto)

这种方式是声明式的。你需要在实体类的字段上使用 @Relation 注解来定义关联关系,之后在查询时,MyBatis-Flex 会自动帮你完成数据的填充。这是最常用和推荐的方式。

@Relation 注解支持多种关联类型:

关联类型注解值说明
一对一RelationType.OneToOne例如:一个员工对应一个工位
多对一RelationType.ManyToOne例如:多本书属于同一个作者
一对多RelationType.OneToMany例如:一个作者拥有多本书
多对多RelationType.ManyToMany例如:一个学生选修多门课程,一门课程有多个学生

语法与示例

a. 一对多 (OneToMany)

Account 实体类中,定义一个 List<Book> 字段,并加上注解。

public class Account {
    private Long id;
    private String userName;
    
    // 关键:使用 @Relation 注解定义一对多关系
    @Relation(
        value = RelationType.OneToMany, // 关联类型
        targetField = "accountId" // 目标表(Book)中的关联字段
    )
    private List<Book> books;
    // ... getters and setters
}

public class Book {
    private Long id;
    private String title;
    private Long accountId; // 这个字段与 Account.id 关联
    // ... getters and setters
}

查询时,直接使用普通查询即可,关联数据会自动填充:

// 无需在 QueryWrapper 中写 join,直接查
QueryWrapper query = QueryWrapper.create()
    .where(Account::getUserName).eq("张三");
    
List<Account> accounts = accountMapper.selectListByQuery(query);
// 此时,每个 accounts 中的 Account 对象,其 books 属性已被自动填充

b. 多对一 (ManyToOne)

Book 实体类中,定义一个 Account 字段。

public class Book {
    private Long id;
    private String title;
    private Long accountId;
    
    // 关键:使用 @Relation 注解定义多对一关系
    @Relation(
        value = RelationType.ManyToOne, // 关联类型
        targetField = "accountId", // 当前表(Book)中的关联字段
        // 指定目标表的关联字段,默认为 id,可省略
        targetFieldBind = "id" 
    )
    private Account account;
    // ... getters and setters
}

查询书籍时,作者信息会自动填充:

List<Book> books = bookMapper.selectAll();
// 每个 Book 对象的 account 属性已被自动填充

c. 多对多 (ManyToMany)

多对多需要一个中间表。例如 Student, Course, StudentCourse

public class Student {
    private Long id;
    private String name;
    
    @Relation(
        value = RelationType.ManyToMany,
        // 中间表实体类
        targetTable = "tb_student_course",
        // 当前对象在中间表中的字段
        selfField = "id",
        // 当前对象在中间表中的字段(selfField)对应的中间表字段
        selfTargetField = "student_id",
        // 目标对象在中间表中的字段(targetField)对应的中间表字段
        targetTargetField = "course_id",
        // 目标表实体类
        targetTableField = "tb_course",
        // 目标对象的关联字段(通常是id)
        targetField = "id"
    )
    private List<Course> courses;
}

3. 关联查询与条件

无论是哪种方式,你都可以轻松地添加关联表的查询条件。

使用 QueryWrapper 添加关联表条件:

QueryWrapper query = QueryWrapper.create()
    .select()
    .from(Account.class)
    // 定义关联关系
    .leftJoin(Book.class).on(Account::getId).eq(Book::getAccountId))
    // 主表条件
    .where(Account::getUserName).like("张")
    // 关联表条件!!!
    .and(Book::getPrice).gt(new BigDecimal("50.00"))
    .and(Book::getStatus).eq(1);

List<Account> accounts = accountMapper.selectListByQuery(query);

使用 @Relation 时,通过 extraCondition 添加额外条件:

public class Account {
    @Relation(
        value = RelationType.OneToMany,
        targetField = "accountId",
        // 额外条件:只查询状态为已发布的书籍
        extraCondition = "status = 1 and price > 50"
    )
    private List<Book> publishedExpensiveBooks;
}

总结对比

特性基于 QueryWrapper基于 @Relation 注解
灵活性高,可动态构建复杂关联中,关系在代码中预先定义
便捷性低,每次查询需手动编写关联高,定义一次,随处使用
代码侵入性无,不修改实体类有,需要在实体类中添加注解和字段
适用场景动态、复杂的关联查询固定的、常见的关联关系

最佳实践建议:

  • 对于业务中固定的关联关系(如查询用户时总要显示他的订单),使用 @Relation 注解
  • 对于动态的、一次性的复杂关联查询(如根据多个动态条件关联查询),使用 QueryWrapper 手动关联
  • 灵活处理复杂关联使用基于QueryWrapper的关联查询 (Manual),配置即可实现关联查询使用基于 @Relation注解的关联查询 (Auto)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 利用Log4j将不同Package的日志输出到不同文件的方法

    利用Log4j将不同Package的日志输出到不同文件的方法

    日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。这篇文章主要介绍了利用Log4j将不同Package的日志输出到不同文件的方法,需要的朋友可以参考借鉴,下面来跟着小编一起学习学习吧。
    2017-01-01
  • GSON框架下将百度天气JSON数据转JavaBean

    GSON框架下将百度天气JSON数据转JavaBean

    这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下
    2025-08-08
  • idea日志乱码和tomcat日志乱码问题的解决方法

    idea日志乱码和tomcat日志乱码问题的解决方法

    这篇文章主要介绍了idea日志乱码和tomcat日志乱码问题的解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • 一文讲解如何优雅的调试jar包

    一文讲解如何优雅的调试jar包

    在现实开发过程中,现场环境永远比开发环境复杂,下面这篇文章主要给大家介绍了关于如何优雅的调试jar包的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-03-03
  • 详解JAVA 七种创建单例的方法

    详解JAVA 七种创建单例的方法

    这篇文章主要介绍了详解JAVA 七种创建单例的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • SpringMvc静态资源访问实现方法代码实例

    SpringMvc静态资源访问实现方法代码实例

    这篇文章主要介绍了SpringMvc静态资源访问实现方法代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • SpringBoot定制三种错误页面及错误数据方法示例

    SpringBoot定制三种错误页面及错误数据方法示例

    Spring Boot提供的默认异常处理机制通常并不一定适合我们实际的业务场景,因此,我们通常会根据自身的需要对Spring Boot全局异常进行统一定制,例如定制错误页面,定制错误数据等。本文主要介绍了SpringBoot三种自定义错误页面的实现,快来学习吧
    2021-12-12
  • spring boot异步(Async)任务调度实现方法

    spring boot异步(Async)任务调度实现方法

    在没有使用spring boot之前,我们的做法是在配置文件中定义一个任务池,然后将@Async注解的任务丢到任务池中去执行,那么在spring boot中,怎么来实现异步任务的调用了,下面通过本文给大家讲解,需要的朋友参考下
    2018-02-02
  • Layui 后台加载菜单栏名称以及url的例子

    Layui 后台加载菜单栏名称以及url的例子

    今天小编就为大家分享一篇Layui 后台加载菜单栏名称以及url的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-09-09
  • Spring中allowedOriginPatterns和allowedOrigins方法有何不同详解

    Spring中allowedOriginPatterns和allowedOrigins方法有何不同详解

    这篇文章主要给大家介绍了关于Spring中allowedOriginPatterns和allowedOrigins方法有何不同,allowedOriginPatterns和allowedOrigins都是用来设置允许跨域请求的来源,需要的朋友可以参考下
    2023-10-10

最新评论