最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入解析MybatisPlus多表連接查詢

 更新時(shí)間:2024年06月21日 11:32:54   作者:熱心碼民阿振  
在一些復(fù)雜的業(yè)務(wù)場景中,我們經(jīng)常會遇到多表連接查詢的需求,本文主要介紹了深入解析MybatisPlus多表連接查詢,具有一定的參考價(jià)值,感興趣的可以了解一下

一、序言

在開發(fā)過程中,數(shù)據(jù)庫查詢是非常常見的操作。而在一些復(fù)雜的業(yè)務(wù)場景中,我們經(jīng)常會遇到多表連接查詢的需求。針對這種情況,MybatisPlus提供了一系列強(qiáng)大的功能來支持一對一查詢、一對多查詢和多對多查詢。無論是查詢單條記錄還是查詢多條記錄,MybatisPlus都能提供靈活的解決方案。

通過一對一查詢,我們可以方便地從主表中獲取關(guān)聯(lián)表的詳細(xì)信息;一對多查詢能夠幫助我們從一個(gè)主表中獲取多個(gè)關(guān)聯(lián)表的記錄;而多對多查詢則可以輕松地獲取多個(gè)表之間的交叉組合。在實(shí)現(xiàn)這些查詢時(shí),MybatisPlus提供了豐富的注解和接口來簡化我們的開發(fā)工作。通過合理的配置,我們可以高效地完成各種類型的多表連接查詢操作,提升應(yīng)用程序的性能和開發(fā)效率。

?接下來的文章中,我們將深入探討以上查詢類型的具體實(shí)現(xiàn)方法,以及一些常見的注意事項(xiàng)。通過學(xué)習(xí)和理解這些內(nèi)容,相信您能對MybatisPlus多表連接查詢有更深入的了解,為您的項(xiàng)目開發(fā)提供有力的支持。

1. 組件及版本說明

  • SpringBoot 2.6.x
  • MyBatisPlus 3.5.x
  • Mysql 5.7.x

2. 場景

為了說明連接查詢的關(guān)系,這里以學(xué)生、課程及其關(guān)系為示例。

image-20240123155450046

3. 前期準(zhǔn)備

此部分需要讀者掌握以下內(nèi)容:Lambda 表達(dá)式、特別是方法引用;函數(shù)式接口;流式運(yùn)算等等,否則理解起來會有些吃力。實(shí)體類與 Vo 的映射關(guān)系,作者創(chuàng)造性的引入特別構(gòu)造器,合理利用繼承關(guān)系,極大的方便了開發(fā)者完成實(shí)體類向 Vo 的轉(zhuǎn)換。空指針異常忽略不處理,借助Java8 新特性的Optional類實(shí)現(xiàn)。

2

二、一對一查詢

一對一查詢最典型的應(yīng)用場景是將id替換成name,比如將userId替換成userName。

(一)查詢單條記錄

查詢單條記錄是指返回值僅有一條記錄,通常是以唯一索引作為條件的返回查詢結(jié)果。

1、示例代碼

/**
 * 查詢單個(gè)學(xué)生信息(一個(gè)學(xué)生對應(yīng)一個(gè)部門)
 */
public UserVo getOneUser(Integer userId) {
    LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
        .eq(User::getUserId, userId);
    // 先查詢用戶信息
    User user = userMapper.selectOne(wrapper);
    // 轉(zhuǎn)化為Vo
    UserVo userVo = Optional.ofNullable(user).map(UserVo::new).orElse(null);
    // 從其它表查詢信息再封裝到Vo
    Optional.ofNullable(userVo).ifPresent(this::addDetpNameInfo);
    return userVo;
}

附屬函數(shù)addDetpNameInfo代碼補(bǔ)充

/**
 * 補(bǔ)充部門名稱信息
 */
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、理論分析

查詢單個(gè)實(shí)體共分為兩個(gè)步驟:根據(jù)條件查詢主表數(shù)據(jù)(需處理空指針異常);封裝 Vo 并查詢附屬表數(shù)據(jù)。

查詢結(jié)果(VO)只有一條記錄,需要查詢兩次數(shù)據(jù)庫,時(shí)間復(fù)雜度為O(1)。

(二)查詢多條記錄

查詢多條記錄是指查詢結(jié)果為列表,通常是指以普通索引為條件的查詢結(jié)果。

1、示例代碼

/**
 * 批量查詢學(xué)生信息(一個(gè)學(xué)生對應(yīng)一個(gè)部門)
 */
public List<UserVo> getUserByList() {
    // 先查詢用戶信息(表現(xiàn)形式為列表)
    List<User> user = userMapper.selectList(Wrappers.emptyWrapper());
    List<UserVo> userVos = user.stream().map(UserVo::new).collect(toList());
    // 此步驟可以有多個(gè)
    addDeptNameInfo(userVos);
    return userVos;
}

附屬函數(shù)addDeptNameInfo代碼補(bǔ)充

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));
    // 構(gòu)造映射關(guān)系,方便匹配deptId與deptName
    Map<Integer, String> hashMap = dept.stream().collect(toMap(Dept::getDeptId, Dept::getDeptName));
    // 封裝Vo,并添加到集合中(關(guān)鍵內(nèi)容)
    userVos.forEach(e -> e.setDeptName(hashMap.get(e.getDeptId())));
}

2、理論分析

先查詢包含id的列表記錄,從結(jié)果集中析出id并轉(zhuǎn)化成批查詢語句再訪問數(shù)據(jù)庫,從第二次調(diào)用結(jié)果集中解析出name

查詢結(jié)果(VO)有多條記錄,但僅調(diào)用兩次數(shù)據(jù)庫,時(shí)間復(fù)雜度為O(1)。

三、一對多查詢

一對多查詢最常見的場景是查詢部門所包含的學(xué)生信息,由于一個(gè)部門對應(yīng)多個(gè)學(xué)生,每個(gè)學(xué)生對應(yīng)一個(gè)部門,因此稱為一對多查詢。

(一)查詢單條記錄

1、示例代碼

/**
 * 查詢單個(gè)部門(其中一個(gè)部門有多個(gè)用戶)
 */
public DeptVo getOneDept(Integer deptId) {
    // 查詢部門基礎(chǔ)信息
    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代碼補(bǔ)充

private void addUserInfo(DeptVo deptVo) {
    // 根據(jù)部門deptId查詢學(xué)生列表
    LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class).eq(User::getDeptId, deptVo.getDeptId());
    List<User> users = userMapper.selectList(wrapper);
    deptVo.setUsers(users);
}

2、理論分析

整個(gè)過程共分為兩個(gè)階段:通過部門表中主鍵查詢指定部門信息,通過學(xué)生表中部門ID外鍵查詢學(xué)生信息,將結(jié)果合并,形成返回值(Vo)。

一對多查詢單條記錄整個(gè)過程至多需要調(diào)用2次數(shù)據(jù)庫查詢,查詢次數(shù)為常數(shù),查詢時(shí)間復(fù)雜度為O(1)

(二)查詢多條記錄

1、示例代碼

/**
 * 查詢多個(gè)部門(其中一個(gè)部門有多個(gè)用戶)
 */
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代碼補(bǔ)充

private void addUserInfo(List<DeptVo> deptVos) {
    // 準(zhǔn)備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));
    // 重點(diǎn):將用戶按照deptId分組
    Map<Integer, List<User>> hashMap = users.stream().collect(groupingBy(User::getDeptId));
    // 合并結(jié)果,構(gòu)造Vo,添加集合列表
    deptVos.forEach(e -> e.setUsers(hashMap.get(e.getDeptId())));
}

2、理論分析

整個(gè)過程共分為三個(gè)階段:通過普通索引從部門表中查詢?nèi)舾蓷l記錄;將部門ID轉(zhuǎn)化為批查詢從學(xué)生表中查詢學(xué)生記錄;將學(xué)生記錄以部門ID為單位進(jìn)行分組,合并結(jié)果,轉(zhuǎn)化為Vo。

一對多查詢多條記錄需要調(diào)用2次數(shù)據(jù)庫查詢,查詢次數(shù)為常數(shù),查詢時(shí)間復(fù)雜度為O(1)

四、多對多查詢

MybatisPlus 實(shí)現(xiàn)多對多查詢是一件極富挑戰(zhàn)性的任務(wù),也是連接查詢中最困難的部分。

以空間置換時(shí)間,借助于流式運(yùn)算,解決多對多查詢難題。

多對多查詢相對于一對多查詢,增加了流式分組運(yùn)算、批量 HashMap 取值等內(nèi)容。

3

(一)查詢單條記錄

查詢單條記錄一般是指通過兩個(gè)查詢條件查詢出一條匹配表中的記錄。

1、示例代碼

public StudentVo getStudent(Integer stuId) {
    // 通過主鍵查詢學(xué)生信息
    StudentVo studentVo = ConvertUtils.convertObj(getById(stuId), StudentVo::new);
    LambdaQueryWrapper<StuSubRelation> wrapper = Wrappers.lambdaQuery(StuSubRelation.class).eq(StuSubRelation::getStuId, stuId);
    // 查詢匹配關(guān)系
    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次,先查詢學(xué)生信息,然后查詢學(xué)生與課程匹配信息,最后查詢課程分?jǐn)?shù)信息,查詢時(shí)間復(fù)雜度為O(1)。

(二)查詢多條記錄

1、示例代碼

public List<StudentVo> getStudentList() {
    // 通過主鍵查詢學(xué)生信息
    List<StudentVo> studentVoList = EntityUtils.toList(list(), StudentVo::new);
    // 批量查詢學(xué)生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()));
            // 填充分?jǐn)?shù)
            list.forEach(e -> e.setScore(table.get(studentVo.getId(), e.getId())));
            studentVo.setSubList(list);
        }
    }
    return studentVoList;
}

2、理論分析

多對多N條記錄查詢由于使用了批查詢,因此最多訪問數(shù)據(jù)庫也是3次,先查詢學(xué)生信息,然后查詢學(xué)生與課程匹配信息,最后查詢課程分?jǐn)?shù)信息,查詢時(shí)間復(fù)雜度為O(1)。

五、總結(jié)和拓展

1、總結(jié)

通過上述分析,能夠用 MybatisPlus 解決多表連接查詢中的一對一一對多、多對多查詢。

  • 上述代碼行文緊湊,充分利用 IDE 對 Lambda 表達(dá)式的支持,在編譯期間完成對代碼的檢查。
  • 業(yè)務(wù)邏輯清晰,可維護(hù)性、可修改性優(yōu)勢明顯。
  • 一次查詢需要訪問至多兩次數(shù)據(jù)庫,時(shí)間復(fù)雜度為o(1),主鍵查詢或者索引查詢,查詢效率高。

2、拓展

MybatisPlus能很好的解決單表查詢問題,同時(shí)借助在單表查詢的封裝能很好地解決連接查詢問題。

本方案不僅解決了連接查詢問題,同時(shí)具備如下內(nèi)容拓展:

  • 當(dāng)數(shù)據(jù)量較大時(shí),仍然具有穩(wěn)定的查詢效率

    當(dāng)數(shù)據(jù)量達(dá)到百萬級別時(shí),傳統(tǒng)的單表通過索引查詢已經(jīng)面臨挑戰(zhàn),普通的多表連接查詢性能隨著數(shù)據(jù)量的遞增呈現(xiàn)指數(shù)級下降。

    本方案通過將連接查詢轉(zhuǎn)化為主鍵(索引)查詢,查詢性能等效于單表查詢。

  • 與二級緩存配合使用進(jìn)一步提高查詢效率

    當(dāng)所有的查詢均轉(zhuǎn)化為以單表為基礎(chǔ)的查詢后,方能安全的引入二級緩存。二級緩存的單表增刪改查操作自適應(yīng)聯(lián)動,解決了二級緩存的臟數(shù)據(jù)問題。

到此這篇關(guān)于深入解析MybatisPlus多表連接查詢的文章就介紹到這了,更多相關(guān)MybatisPlus多表連接查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • 解決Springboot整合shiro時(shí)靜態(tài)資源被攔截的問題

    解決Springboot整合shiro時(shí)靜態(tài)資源被攔截的問題

    這篇文章主要介紹了解決Springboot整合shiro時(shí)靜態(tài)資源被攔截的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • Java Synchronized的偏向鎖詳細(xì)分析

    Java Synchronized的偏向鎖詳細(xì)分析

    synchronized作為Java程序員最常用同步工具,很多人卻對它的用法和實(shí)現(xiàn)原理一知半解,以至于還有不少人認(rèn)為synchronized是重量級鎖,性能較差,盡量少用。但不可否認(rèn)的是synchronized依然是并發(fā)首選工具,本文就來詳細(xì)講講
    2023-04-04
  • springcloud項(xiàng)目改名的操作方法

    springcloud項(xiàng)目改名的操作方法

    這篇文章主要介紹了springcloud項(xiàng)目改名的操作方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • JAVA音頻處理依賴庫示例操作大全(從格式轉(zhuǎn)換到音頻拼接)

    JAVA音頻處理依賴庫示例操作大全(從格式轉(zhuǎn)換到音頻拼接)

    在現(xiàn)代應(yīng)用開發(fā)中,音頻處理是常見需求,包括格式轉(zhuǎn)換、音頻拼接、剪輯、降噪等操作,本教程將介紹Spring?Boot中常用的音頻處理依賴庫,比較它們的特性、區(qū)別及社區(qū)活躍程度,并提供實(shí)用代碼示例,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • java實(shí)現(xiàn)ip地址與十進(jìn)制數(shù)相互轉(zhuǎn)換

    java實(shí)現(xiàn)ip地址與十進(jìn)制數(shù)相互轉(zhuǎn)換

    本文介紹在java中IP地址轉(zhuǎn)換十進(jìn)制數(shù)及把10進(jìn)制再轉(zhuǎn)換成IP地址的方法及實(shí)例參考,曬出來和大家分享一下
    2012-12-12
  • Spring boot實(shí)現(xiàn)文件上傳功能

    Spring boot實(shí)現(xiàn)文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了Spring boot實(shí)現(xiàn)文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • Java實(shí)現(xiàn)推箱子游戲

    Java實(shí)現(xiàn)推箱子游戲

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)推箱子游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Spring Boot RestTemplate提交表單數(shù)據(jù)的三種方法

    Spring Boot RestTemplate提交表單數(shù)據(jù)的三種方法

    本篇文章主要介紹了Spring Boot RestTemplate提交表單數(shù)據(jù)的三種方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-03-03
  • java項(xiàng)目啟動失敗的問題及解決

    java項(xiàng)目啟動失敗的問題及解決

    這篇文章主要介紹了java項(xiàng)目啟動失敗的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • springboot整合freemarker的踩坑及解決

    springboot整合freemarker的踩坑及解決

    這篇文章主要介紹了springboot整合freemarker的踩坑及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05

最新評論

天镇县| 和龙市| 当涂县| 无棣县| 玛纳斯县| 苍山县| 洪湖市| 滕州市| 翁源县| 荣昌县| 礼泉县| 灌阳县| 汕头市| 萝北县| 阿荣旗| 循化| 绥芬河市| 友谊县| 西城区| 垫江县| 无锡市| 张掖市| 中江县| 睢宁县| 县级市| 贵阳市| 金山区| 大英县| 垣曲县| 定陶县| 伽师县| 龙游县| 佛冈县| 精河县| 丹阳市| 盐亭县| 达日县| 济阳县| 皋兰县| 呼和浩特市| 文登市|