java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis-Flex多表关联查询

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

作者:友莘居士

本文介绍了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);

关键点:

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

最佳实践建议:

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

您可能感兴趣的文章:
阅读全文