深入解析MybatisPlus多表連接查詢
一、序言
在開發(fā)過程中,數(shù)據(jù)庫查詢是非常常見的操作。而在一些復雜的業(yè)務場景中,我們經常會遇到多表連接查詢的需求。針對這種情況,MybatisPlus提供了一系列強大的功能來支持一對一查詢、一對多查詢和多對多查詢。無論是查詢單條記錄還是查詢多條記錄,MybatisPlus都能提供靈活的解決方案。
通過一對一查詢,我們可以方便地從主表中獲取關聯(lián)表的詳細信息;一對多查詢能夠幫助我們從一個主表中獲取多個關聯(lián)表的記錄;而多對多查詢則可以輕松地獲取多個表之間的交叉組合。在實現(xiàn)這些查詢時,MybatisPlus提供了豐富的注解和接口來簡化我們的開發(fā)工作。通過合理的配置,我們可以高效地完成各種類型的多表連接查詢操作,提升應用程序的性能和開發(fā)效率。
?接下來的文章中,我們將深入探討以上查詢類型的具體實現(xiàn)方法,以及一些常見的注意事項。通過學習和理解這些內容,相信您能對MybatisPlus多表連接查詢有更深入的了解,為您的項目開發(fā)提供有力的支持。
1. 組件及版本說明
- SpringBoot 2.6.x
- MyBatisPlus 3.5.x
- Mysql 5.7.x
2. 場景
為了說明連接查詢的關系,這里以學生、課程及其關系為示例。
3. 前期準備
此部分需要讀者掌握以下內容:Lambda
表達式、特別是方法引用;函數(shù)式接口;流式運算等等,否則理解起來會有些吃力。實體類與 Vo 的映射關系,作者創(chuàng)造性的引入特別構造器,合理利用繼承關系,極大的方便了開發(fā)者完成實體類向 Vo 的轉換??罩羔槷惓:雎圆惶幚恚柚鶭ava8 新特性的Optional
類實現(xiàn)。
二、一對一查詢
一對一查詢最典型的應用場景是將id
替換成name
,比如將userId
替換成userName
。
(一)查詢單條記錄
查詢單條記錄是指返回值僅有一條記錄,通常是以唯一索引作為條件的返回查詢結果。
1、示例代碼
/** * 查詢單個學生信息(一個學生對應一個部門) */ public UserVo getOneUser(Integer userId) { LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class) .eq(User::getUserId, userId); // 先查詢用戶信息 User user = userMapper.selectOne(wrapper); // 轉化為Vo UserVo userVo = Optional.ofNullable(user).map(UserVo::new).orElse(null); // 從其它表查詢信息再封裝到Vo Optional.ofNullable(userVo).ifPresent(this::addDetpNameInfo); return userVo; }
附屬函數(shù)addDetpNameInfo
代碼補充
/** * 補充部門名稱信息 */ private void addDetpNameInfo(UserVo userVo) { LambdaQueryWrapper<Dept> wrapper = Wrappers.lambdaQuery(Dept.class) .eq(Dept::getDeptId, userVo.getDeptId()); Dept dept = deptMapper.selectOne(wrapper); Optional.ofNullable(dept).ifPresent(e -> userVo.setDeptName(e.getDeptName())); }
2、理論分析
查詢單個實體共分為兩個步驟:根據(jù)條件查詢主表數(shù)據(jù)(需處理空指針異常);封裝 Vo 并查詢附屬表數(shù)據(jù)。
查詢結果(VO)只有一條記錄,需要查詢兩次數(shù)據(jù)庫,時間復雜度為O(1)
。
(二)查詢多條記錄
查詢多條記錄是指查詢結果為列表,通常是指以普通索引為條件的查詢結果。
1、示例代碼
/** * 批量查詢學生信息(一個學生對應一個部門) */ public List<UserVo> getUserByList() { // 先查詢用戶信息(表現(xiàn)形式為列表) List<User> user = userMapper.selectList(Wrappers.emptyWrapper()); List<UserVo> userVos = user.stream().map(UserVo::new).collect(toList()); // 此步驟可以有多個 addDeptNameInfo(userVos); return userVos; }
附屬函數(shù)addDeptNameInfo
代碼補充
private void addDeptNameInfo(List<UserVo> userVos) { // 提取用戶userId,方便批量查詢 Set<Integer> deptIds = userVos.stream().map(User::getDeptId).collect(toSet()); // 根據(jù)deptId查詢deptName(查詢前,先做非空判斷) List<Dept> dept = deptMapper.selectList(Wrappers.lambdaQuery(Dept.class).in(Dept::getDeptId, deptIds)); // 構造映射關系,方便匹配deptId與deptName Map<Integer, String> hashMap = dept.stream().collect(toMap(Dept::getDeptId, Dept::getDeptName)); // 封裝Vo,并添加到集合中(關鍵內容) userVos.forEach(e -> e.setDeptName(hashMap.get(e.getDeptId()))); }
2、理論分析
先查詢包含id
的列表記錄,從結果集中析出id
并轉化成批查詢語句再訪問數(shù)據(jù)庫,從第二次調用結果集中解析出name
。
查詢結果(VO)有多條記錄,但僅調用兩次數(shù)據(jù)庫,時間復雜度為O(1)
。
三、一對多查詢
一對多查詢最常見的場景是查詢部門所包含的學生信息,由于一個部門對應多個學生,每個學生對應一個部門,因此稱為一對多查詢。
(一)查詢單條記錄
1、示例代碼
/** * 查詢單個部門(其中一個部門有多個用戶) */ public DeptVo getOneDept(Integer deptId) { // 查詢部門基礎信息 LambdaQueryWrapper<Dept> wrapper = Wrappers.lambdaQuery(Dept.class).eq(Dept::getDeptId, deptId); DeptVo deptVo = Optional.ofNullable(deptMapper.selectOne(wrapper)).map(DeptVo::new).orElse(null); Optional.ofNullable(deptVo).ifPresent(this::addUserInfo); return deptVo; }
附屬函數(shù)addUserInfo
代碼補充
private void addUserInfo(DeptVo deptVo) { // 根據(jù)部門deptId查詢學生列表 LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class).eq(User::getDeptId, deptVo.getDeptId()); List<User> users = userMapper.selectList(wrapper); deptVo.setUsers(users); }
2、理論分析
整個過程共分為兩個階段:通過部門表中主鍵查詢指定部門信息,通過學生表中部門ID外鍵查詢學生信息,將結果合并,形成返回值(Vo)。
一對多查詢單條記錄整個過程至多需要調用2次數(shù)據(jù)庫查詢,查詢次數(shù)為常數(shù),查詢時間復雜度為O(1)
。
(二)查詢多條記錄
1、示例代碼
/** * 查詢多個部門(其中一個部門有多個用戶) */ public List<DeptVo> getDeptByList() { // 按條件查詢部門信息 List<Dept> deptList = deptMapper.selectList(Wrappers.emptyWrapper()); List<DeptVo> deptVos = deptList.stream().map(DeptVo::new).collect(toList()); if (deptVos.size() > 0) { addUserInfo(deptVos); } return deptVos; }
附屬函數(shù)addUserInfo
代碼補充
private void addUserInfo(List<DeptVo> deptVos) { // 準備deptId方便批量查詢用戶信息 Set<Integer> deptIds = deptVos.stream().map(Dept::getDeptId).collect(toSet()); // 用批量deptId查詢用戶信息 List<User> users = userMapper.selectList(Wrappers.lambdaQuery(User.class).in(User::getDeptId, deptIds)); // 重點:將用戶按照deptId分組 Map<Integer, List<User>> hashMap = users.stream().collect(groupingBy(User::getDeptId)); // 合并結果,構造Vo,添加集合列表 deptVos.forEach(e -> e.setUsers(hashMap.get(e.getDeptId()))); }
2、理論分析
整個過程共分為三個階段:通過普通索引從部門表中查詢若干條記錄;將部門ID轉化為批查詢從學生表中查詢學生記錄;將學生記錄以部門ID為單位進行分組,合并結果,轉化為Vo。
一對多查詢多條記錄需要調用2次數(shù)據(jù)庫查詢,查詢次數(shù)為常數(shù),查詢時間復雜度為O(1)
。
四、多對多查詢
MybatisPlus 實現(xiàn)多對多查詢是一件極富挑戰(zhàn)性的任務,也是連接查詢中最困難的部分。
以空間置換時間,借助于流式運算,解決多對多查詢難題。
多對多查詢相對于一對多查詢,增加了流式分組運算、批量 HashMap 取值等內容。
(一)查詢單條記錄
查詢單條記錄一般是指通過兩個查詢條件查詢出一條匹配表中的記錄。
1、示例代碼
public StudentVo getStudent(Integer stuId) { // 通過主鍵查詢學生信息 StudentVo studentVo = ConvertUtils.convertObj(getById(stuId), StudentVo::new); LambdaQueryWrapper<StuSubRelation> wrapper = Wrappers.lambdaQuery(StuSubRelation.class).eq(StuSubRelation::getStuId, stuId); // 查詢匹配關系 List<StuSubRelation> stuSubRelations = stuSubRelationMapper.selectList(wrapper); Set<Integer> subIds = stuSubRelations.stream().map(StuSubRelation::getSubId).collect(toSet()); if (studentVo != null && subIds.size() > 0) { List<Subject> subList = subjectMapper.selectList(Wrappers.lambdaQuery(Subject.class).in(Subject::getId, subIds)); List<SubjectBo> subBoList = EntityUtils.toList(subList, SubjectBo::new); HashBasedTable<Integer, Integer, Integer> table = getHashBasedTable(stuSubRelations); subBoList.forEach(e -> e.setScore(table.get(stuId, e.getId()))); studentVo.setSubList(subBoList); } return studentVo; }
2、理論分析
多對多單條記錄查詢最多訪問數(shù)據(jù)庫3次,先查詢學生信息,然后查詢學生與課程匹配信息,最后查詢課程分數(shù)信息,查詢時間復雜度為O(1)
。
(二)查詢多條記錄
1、示例代碼
public List<StudentVo> getStudentList() { // 通過主鍵查詢學生信息 List<StudentVo> studentVoList = EntityUtils.toList(list(), StudentVo::new); // 批量查詢學生ID Set<Integer> stuIds = studentVoList.stream().map(Student::getId).collect(toSet()); LambdaQueryWrapper<StuSubRelation> wrapper = Wrappers.lambdaQuery(StuSubRelation.class).in(StuSubRelation::getStuId, stuIds); List<StuSubRelation> stuSubRelations = stuSubRelationMapper.selectList(wrapper); // 批量查詢課程ID Set<Integer> subIds = stuSubRelations.stream().map(StuSubRelation::getSubId).collect(toSet()); if (stuIds.size() > 0 && subIds.size() > 0) { HashBasedTable<Integer, Integer, Integer> table = getHashBasedTable(stuSubRelations); List<Subject> subList = subjectMapper.selectList(Wrappers.lambdaQuery(Subject.class).in(Subject::getId, subIds)); List<SubjectBo> subjectBoList = EntityUtils.toList(subList, SubjectBo::new); Map<Integer, List<Integer>> map = stuSubRelations.stream().collect(groupingBy(StuSubRelation::getStuId, mapping(StuSubRelation::getSubId, toList()))); for (StudentVo studentVo : studentVoList) { // 獲取課程列表 List<SubjectBo> list = ListUtils.select(subjectBoList, e -> emptyIfNull(map.get(studentVo.getId())).contains(e.getId())); // 填充分數(shù) list.forEach(e -> e.setScore(table.get(studentVo.getId(), e.getId()))); studentVo.setSubList(list); } } return studentVoList; }
2、理論分析
多對多N條記錄查詢由于使用了批查詢,因此最多訪問數(shù)據(jù)庫也是3次,先查詢學生信息,然后查詢學生與課程匹配信息,最后查詢課程分數(shù)信息,查詢時間復雜度為O(1)
。
五、總結和拓展
1、總結
通過上述分析,能夠用 MybatisPlus 解決多表連接查詢中的一對一
、一對多
、多對多
查詢。
- 上述代碼行文緊湊,充分利用 IDE 對 Lambda 表達式的支持,在編譯期間完成對代碼的檢查。
- 業(yè)務邏輯清晰,可維護性、可修改性優(yōu)勢明顯。
- 一次查詢需要訪問至多兩次數(shù)據(jù)庫,時間復雜度為
o(1)
,主鍵查詢或者索引查詢,查詢效率高。
2、拓展
MybatisPlus能很好的解決單表查詢問題,同時借助在單表查詢的封裝能很好地解決連接查詢問題。
本方案不僅解決了連接查詢問題,同時具備如下內容拓展:
當數(shù)據(jù)量較大時,仍然具有穩(wěn)定的查詢效率
當數(shù)據(jù)量達到百萬級別時,傳統(tǒng)的單表通過索引查詢已經面臨挑戰(zhàn),普通的多表連接查詢性能隨著數(shù)據(jù)量的遞增呈現(xiàn)指數(shù)級下降。
本方案通過將連接查詢轉化為主鍵(索引)查詢,查詢性能等效于單表查詢。
與二級緩存配合使用進一步提高查詢效率
當所有的查詢均轉化為以單表為基礎的查詢后,方能安全的引入二級緩存。二級緩存的單表增刪改查操作自適應聯(lián)動,解決了二級緩存的臟數(shù)據(jù)問題。
到此這篇關于深入解析MybatisPlus多表連接查詢的文章就介紹到這了,更多相關MybatisPlus多表連接查詢內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Mybatis-Plus多表關聯(lián)查詢的使用案例解析
- MyBatis多表查詢和注解開發(fā)案例詳解
- mybatis-plus多表分頁查詢最佳實現(xiàn)方法(非常簡單)
- Mybatis-plus實現(xiàn)join連表查詢的示例代碼
- MyBatis中ResultMap與多表查詢的處理方法
- MybatisPlus多表連接查詢的具體實現(xiàn)
- mybatis于xml方式和注解方式實現(xiàn)多表查詢的操作方法
- mybatis-plus多表查詢操作方法
- MyBatis?實現(xiàn)動態(tài)排序的多表查詢
- Mybatis分頁查詢主從表的實現(xiàn)示例
- mybatis連接數(shù)據(jù)庫實現(xiàn)雙表查詢
相關文章
Java 實現(xiàn)實時監(jiān)聽文件夾是否有新文件增加并上傳服務器功能
本文中主要陳述一種實時監(jiān)聽文件夾中是否有文件增加的功能,可用于實際文件上傳功能的開發(fā)。本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友參考下吧2019-09-09Java 重命名 Excel 工作表并設置工作表標簽顏色的示例代碼
這篇文章主要介紹了Java 重命名 Excel 工作表并設置工作表標簽顏色的示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10使用Springboot整合GridFS實現(xiàn)文件操作
這篇文章主要介紹了使用Springboot整合GridFS實現(xiàn)文件操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10詳解Spring boot/Spring 統(tǒng)一錯誤處理方案的使用
這篇文章主要介紹了詳解Spring boot/Spring 統(tǒng)一錯誤處理方案的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06IntelliJ IDEA基于Scala實現(xiàn)Git檢查工具
這篇文章主要介紹了如何使用Scala實現(xiàn)自定義的Git檢查工具,大家可以基于本文的示例進行擴展與實現(xiàn),也可以進行其他應用方向的嘗試,感興趣的可以了解下2023-08-08