MyBatis-Flex多表關(guān)聯(lián)查詢(xún)指南分享
MyBatis-Flex 提供了強(qiáng)大且靈活的多表關(guān)聯(lián)查詢(xún)支持,其核心是通過(guò) QueryWrapper 和 Relations 注解來(lái)實(shí)現(xiàn)的。與 MyBatis-Plus 等框架的關(guān)聯(lián)查詢(xún)不同,MyBatis-Flex 的關(guān)聯(lián)查詢(xún)是在不涉及 JOIN 的 SQL 的情況下,通過(guò)多次查詢(xún)來(lái)實(shí)現(xiàn)的,這種方式在某些場(chǎng)景下(如分頁(yè)、大數(shù)據(jù)量)反而有更好的性能表現(xiàn)。
MyBatis-Flex 的多表關(guān)聯(lián)主要分為兩種類(lèi)型:
- 基于
QueryWrapper的關(guān)聯(lián)查詢(xún)(動(dòng)態(tài)、手動(dòng)) - 基于
@Relation注解的關(guān)聯(lián)查詢(xún)(靜態(tài)、自動(dòng))
1. 基于QueryWrapper的關(guān)聯(lián)查詢(xún) (Manual)
這種方式非常靈活,你可以通過(guò) QueryWrapper 動(dòng)態(tài)地指定關(guān)聯(lián)關(guān)系和查詢(xún)條件。它主要通過(guò) select(...) 和 leftJoin/innerJoin 等方法來(lái)實(shí)現(xiàn),但其底層機(jī)制依然是分開(kāi)查詢(xún)?cè)俳M合。
核心語(yǔ)法示例:
假設(shè)我們有 Account(賬戶)表和 Book(書(shū)籍)表,一個(gè)賬戶對(duì)應(yīng)多本書(shū)。
// 1. 查詢(xún)賬戶及其書(shū)籍(1對(duì)多)
QueryWrapper query = QueryWrapper.create()
.select() // 查詢(xún)主表(Account)的所有字段
.from(Account.class)
// 使用 leftJoin 或 innerJoin 定義關(guān)聯(lián)關(guān)系(注意:這只是定義關(guān)系,不生成SQL JOIN)
.leftJoin(Book.class).on(Account::getId).eq(Book::getAccountId))
// 添加查詢(xún)條件
.where(Account::getUserName).like("張三")
.and(Book::getPublishDate).ge(LocalDate.now().minusYears(1));
// 執(zhí)行查詢(xún)
// 注意:這里返回的仍然是 List<Account>,但MyBatis-Flex會(huì)通過(guò)額外查詢(xún)自動(dòng)填充其Book列表
List<Account> accounts = accountMapper.selectListByQuery(query);
關(guān)鍵點(diǎn):
leftJoin(Book.class)和on(...)方法定義了Account和Book之間的關(guān)聯(lián)關(guān)系。
MyBatis-Flex 看到這個(gè)定義后,會(huì)執(zhí)行以下步驟:
- 首先查詢(xún)滿足條件的
Account列表。 - 然后根據(jù)
on條件中定義的關(guān)聯(lián)關(guān)系(Account.id = Book.account_id),再發(fā)起一次查詢(xún),獲取所有相關(guān)的Book。 - 最后,自動(dòng)將
Book列表設(shè)置到對(duì)應(yīng)Account對(duì)象的books屬性中(需要你有這個(gè)屬性)。
2. 基于@Relation注解的關(guān)聯(lián)查詢(xún) (Auto)
這種方式是聲明式的。你需要在實(shí)體類(lèi)的字段上使用 @Relation 注解來(lái)定義關(guān)聯(lián)關(guān)系,之后在查詢(xún)時(shí),MyBatis-Flex 會(huì)自動(dòng)幫你完成數(shù)據(jù)的填充。這是最常用和推薦的方式。
@Relation 注解支持多種關(guān)聯(lián)類(lèi)型:
| 關(guān)聯(lián)類(lèi)型 | 注解值 | 說(shuō)明 |
|---|---|---|
| 一對(duì)一 | RelationType.OneToOne | 例如:一個(gè)員工對(duì)應(yīng)一個(gè)工位 |
| 多對(duì)一 | RelationType.ManyToOne | 例如:多本書(shū)屬于同一個(gè)作者 |
| 一對(duì)多 | RelationType.OneToMany | 例如:一個(gè)作者擁有多本書(shū) |
| 多對(duì)多 | RelationType.ManyToMany | 例如:一個(gè)學(xué)生選修多門(mén)課程,一門(mén)課程有多個(gè)學(xué)生 |
語(yǔ)法與示例
a. 一對(duì)多 (OneToMany)
在 Account 實(shí)體類(lèi)中,定義一個(gè) List<Book> 字段,并加上注解。
public class Account {
private Long id;
private String userName;
// 關(guān)鍵:使用 @Relation 注解定義一對(duì)多關(guān)系
@Relation(
value = RelationType.OneToMany, // 關(guān)聯(lián)類(lèi)型
targetField = "accountId" // 目標(biāo)表(Book)中的關(guān)聯(lián)字段
)
private List<Book> books;
// ... getters and setters
}
public class Book {
private Long id;
private String title;
private Long accountId; // 這個(gè)字段與 Account.id 關(guān)聯(lián)
// ... getters and setters
}
查詢(xún)時(shí),直接使用普通查詢(xún)即可,關(guān)聯(lián)數(shù)據(jù)會(huì)自動(dòng)填充:
// 無(wú)需在 QueryWrapper 中寫(xiě) join,直接查
QueryWrapper query = QueryWrapper.create()
.where(Account::getUserName).eq("張三");
List<Account> accounts = accountMapper.selectListByQuery(query);
// 此時(shí),每個(gè) accounts 中的 Account 對(duì)象,其 books 屬性已被自動(dòng)填充
b. 多對(duì)一 (ManyToOne)
在 Book 實(shí)體類(lèi)中,定義一個(gè) Account 字段。
public class Book {
private Long id;
private String title;
private Long accountId;
// 關(guān)鍵:使用 @Relation 注解定義多對(duì)一關(guān)系
@Relation(
value = RelationType.ManyToOne, // 關(guān)聯(lián)類(lèi)型
targetField = "accountId", // 當(dāng)前表(Book)中的關(guān)聯(lián)字段
// 指定目標(biāo)表的關(guān)聯(lián)字段,默認(rèn)為 id,可省略
targetFieldBind = "id"
)
private Account account;
// ... getters and setters
}
查詢(xún)書(shū)籍時(shí),作者信息會(huì)自動(dòng)填充:
List<Book> books = bookMapper.selectAll(); // 每個(gè) Book 對(duì)象的 account 屬性已被自動(dòng)填充
c. 多對(duì)多 (ManyToMany)
多對(duì)多需要一個(gè)中間表。例如 Student, Course, StudentCourse。
public class Student {
private Long id;
private String name;
@Relation(
value = RelationType.ManyToMany,
// 中間表實(shí)體類(lèi)
targetTable = "tb_student_course",
// 當(dāng)前對(duì)象在中間表中的字段
selfField = "id",
// 當(dāng)前對(duì)象在中間表中的字段(selfField)對(duì)應(yīng)的中間表字段
selfTargetField = "student_id",
// 目標(biāo)對(duì)象在中間表中的字段(targetField)對(duì)應(yīng)的中間表字段
targetTargetField = "course_id",
// 目標(biāo)表實(shí)體類(lèi)
targetTableField = "tb_course",
// 目標(biāo)對(duì)象的關(guān)聯(lián)字段(通常是id)
targetField = "id"
)
private List<Course> courses;
}
3. 關(guān)聯(lián)查詢(xún)與條件
無(wú)論是哪種方式,你都可以輕松地添加關(guān)聯(lián)表的查詢(xún)條件。
使用 QueryWrapper 添加關(guān)聯(lián)表?xiàng)l件:
QueryWrapper query = QueryWrapper.create()
.select()
.from(Account.class)
// 定義關(guān)聯(lián)關(guān)系
.leftJoin(Book.class).on(Account::getId).eq(Book::getAccountId))
// 主表?xiàng)l件
.where(Account::getUserName).like("張")
// 關(guān)聯(lián)表?xiàng)l件?。?!
.and(Book::getPrice).gt(new BigDecimal("50.00"))
.and(Book::getStatus).eq(1);
List<Account> accounts = accountMapper.selectListByQuery(query);
使用 @Relation 時(shí),通過(guò) extraCondition 添加額外條件:
public class Account {
@Relation(
value = RelationType.OneToMany,
targetField = "accountId",
// 額外條件:只查詢(xún)狀態(tài)為已發(fā)布的書(shū)籍
extraCondition = "status = 1 and price > 50"
)
private List<Book> publishedExpensiveBooks;
}
總結(jié)對(duì)比
| 特性 | 基于 QueryWrapper | 基于 @Relation 注解 |
|---|---|---|
| 靈活性 | 高,可動(dòng)態(tài)構(gòu)建復(fù)雜關(guān)聯(lián) | 中,關(guān)系在代碼中預(yù)先定義 |
| 便捷性 | 低,每次查詢(xún)需手動(dòng)編寫(xiě)關(guān)聯(lián) | 高,定義一次,隨處使用 |
| 代碼侵入性 | 無(wú),不修改實(shí)體類(lèi) | 有,需要在實(shí)體類(lèi)中添加注解和字段 |
| 適用場(chǎng)景 | 動(dòng)態(tài)、復(fù)雜的關(guān)聯(lián)查詢(xún) | 固定的、常見(jiàn)的關(guān)聯(lián)關(guān)系 |
最佳實(shí)踐建議:
- 對(duì)于業(yè)務(wù)中固定的關(guān)聯(lián)關(guān)系(如查詢(xún)用戶時(shí)總要顯示他的訂單),使用
@Relation注解。 - 對(duì)于動(dòng)態(tài)的、一次性的復(fù)雜關(guān)聯(lián)查詢(xún)(如根據(jù)多個(gè)動(dòng)態(tài)條件關(guān)聯(lián)查詢(xún)),使用
QueryWrapper手動(dòng)關(guān)聯(lián)。 - 靈活處理復(fù)雜關(guān)聯(lián)使用基于QueryWrapper的關(guān)聯(lián)查詢(xún) (Manual),配置即可實(shí)現(xiàn)關(guān)聯(lián)查詢(xún)使用基于 @Relation注解的關(guān)聯(lián)查詢(xún) (Auto)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
利用Log4j將不同Package的日志輸出到不同文件的方法
日志是應(yīng)用軟件中不可缺少的部分,Apache的開(kāi)源項(xiàng)目log4j是一個(gè)功能強(qiáng)大的日志組件,提供方便的日志記錄。這篇文章主要介紹了利用Log4j將不同Package的日志輸出到不同文件的方法,需要的朋友可以參考借鑒,下面來(lái)跟著小編一起學(xué)習(xí)學(xué)習(xí)吧。2017-01-01
GSON框架下將百度天氣JSON數(shù)據(jù)轉(zhuǎn)JavaBean
這篇文章主要為大家詳細(xì)介紹了如何在GSON框架下實(shí)現(xiàn)將百度天氣JSON數(shù)據(jù)轉(zhuǎn)JavaBean,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-08-08
idea日志亂碼和tomcat日志亂碼問(wèn)題的解決方法
這篇文章主要介紹了idea日志亂碼和tomcat日志亂碼問(wèn)題的解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
SpringMvc靜態(tài)資源訪問(wèn)實(shí)現(xiàn)方法代碼實(shí)例
這篇文章主要介紹了SpringMvc靜態(tài)資源訪問(wèn)實(shí)現(xiàn)方法代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
SpringBoot定制三種錯(cuò)誤頁(yè)面及錯(cuò)誤數(shù)據(jù)方法示例
Spring Boot提供的默認(rèn)異常處理機(jī)制通常并不一定適合我們實(shí)際的業(yè)務(wù)場(chǎng)景,因此,我們通常會(huì)根據(jù)自身的需要對(duì)Spring Boot全局異常進(jìn)行統(tǒng)一定制,例如定制錯(cuò)誤頁(yè)面,定制錯(cuò)誤數(shù)據(jù)等。本文主要介紹了SpringBoot三種自定義錯(cuò)誤頁(yè)面的實(shí)現(xiàn),快來(lái)學(xué)習(xí)吧2021-12-12
spring boot異步(Async)任務(wù)調(diào)度實(shí)現(xiàn)方法
在沒(méi)有使用spring boot之前,我們的做法是在配置文件中定義一個(gè)任務(wù)池,然后將@Async注解的任務(wù)丟到任務(wù)池中去執(zhí)行,那么在spring boot中,怎么來(lái)實(shí)現(xiàn)異步任務(wù)的調(diào)用了,下面通過(guò)本文給大家講解,需要的朋友參考下2018-02-02
Layui 后臺(tái)加載菜單欄名稱(chēng)以及url的例子
今天小編就為大家分享一篇Layui 后臺(tái)加載菜單欄名稱(chēng)以及url的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09
Spring中allowedOriginPatterns和allowedOrigins方法有何不同詳解
這篇文章主要給大家介紹了關(guān)于Spring中allowedOriginPatterns和allowedOrigins方法有何不同,allowedOriginPatterns和allowedOrigins都是用來(lái)設(shè)置允許跨域請(qǐng)求的來(lái)源,需要的朋友可以參考下2023-10-10

