MyBatis-Flex多表关联查询指南
MyBatis-Flex 提供了强大且灵活的多表关联查询支持,其核心是通过 QueryWrapper
和 Relations
注解来实现的。与 MyBatis-Plus 等框架的关联查询不同,MyBatis-Flex 的关联查询是在不涉及 JOIN 的 SQL 的情况下,通过多次查询来实现的,这种方式在某些场景下(如分页、大数据量)反而有更好的性能表现。
MyBatis-Flex 的多表关联主要分为两种类型:
- 基于
QueryWrapper
的关联查询(动态、手动) - 基于
@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(...)
方法定义了Account
和Book
之间的关联关系。- MyBatis-Flex 看到这个定义后,会执行以下步骤:
- 首先查询满足条件的
Account
列表。 - 然后根据
on
条件中定义的关联关系(Account.id = Book.account_id
),再发起一次查询,获取所有相关的Book
。 - 最后,自动将
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)