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

SpringBoot項目實現(xiàn)MyBatis流式查詢的教程詳解

 更新時間:2023年06月20日 10:37:42   作者:SZleoWang  
這篇文章主要介紹了SpringBoot項目如何實現(xiàn)MyBatis的流式查詢,mybatis的流式查詢,有點冷門,實際用的場景比較少,但是在某些特殊場景下,卻是十分有效的一個方法,感興趣的同學可以參考一下

前言

mybatis的流式查詢,有點冷門,實際用的場景比較少,但是在某些特殊場景下,卻是十分有效的一個方法。很多人沒有聽說過,實際上是對mybatis沒有太重視,對mybatis想法還停留一個dao接口對應著mapper里的一個sql,mybatis的關鍵是如何寫好sql以及sql的優(yōu)化上;

其實mybatis遠不止這些,通過這篇文章,和大家一塊來見識一下流式查詢,mybatis相對冷門的神秘面紗。文章的內容將從以下幾個方面展開:

  • 什么是mybatis的流式查詢;

  • Cursor接口的主要方法;

  • 代碼層面如何實現(xiàn);

  • 具體的應用場景;

  • 使用中的一些注意事項;

環(huán)境 配置

  • jdk版本:1.8

  • 開發(fā)工具:Intellij iDEA 2020.1

  • springboot:2.3.9.RELEASE

  • mybatis-spring-boot-starter:2.1.4

什么是mybatis流式查詢?

使用mybatis作為持久層的框架時,通過mybatis執(zhí)行查詢數據的請求執(zhí)行成功后,mybatis返回的結果集不是一個集合或對象,而是一個迭代器,可以通過遍歷迭代器來取出結果集,避免一次性取出大量的數據而占用太多的內存。

Cursor

org.apache.ibatis.cursor.Cursor接口有三個抽象方法,分別是

  • isOpen() :判斷cursor是否正處于打開狀態(tài);

  • isConsumed() :判斷查詢結果是否全部讀取完;

  • getCurrentIndex() :查詢已讀取數據在全部數據里的索引位置;

public interface Cursor<T> extends Closeable, Iterable<T> {
 //判斷cursor是否正處于打開狀態(tài)
 //當返回true,則表示cursor已經開始從數據庫里刷新數據了;
  boolean isOpen();
  //判斷查詢結果是否全部讀取完;
  //當返回true,則表示查詢sql匹配的全部數據都消費完了;
  boolean isConsumed();
   //查詢已讀取數據在全部數據里的索引位置;
   //第一條數據的索引位置為0;當返回索引位置為-1時,則表示已經沒有數據可以讀??;
  int getCurrentIndex();
}

代碼實現(xiàn)

mybatis的所謂流式查詢,就是服務端程序查詢數據的過程中,與遠程數據庫一直保持連接,不斷的去數據庫拉取數據,提交事務并關閉sqlsession后,數據庫連接斷開,停止數據拉取,需要注意的是使用這種方式,需要自己手動維護sqlsession和事務的提交。

1、實現(xiàn)方式很簡單,原來返回的類型是集合或對象,流式查詢返回的的類型Curor,泛型內表示實際的類型,其他沒有變化;

@Mapper
public interface PersonDao {
    Cursor<Person> selectByCursor();
    Integer queryCount();
}
<select id="selectByCursor" resultMap="personMap">
    select * from sys_person order by id desc
</select>
<select id="queryCount" resultType="java.lang.Integer">
    select count(*) from sys_person
</select>

2、dao層向service層返回的是Cursor類型對象,只要不提交關閉sqlsession,服務端程序就可以一直從數據數據庫讀取數據,直到查詢sql匹配到數據全部讀取完;

示例里的主要業(yè)務邏輯是:從sys_person表中讀取所有的人員信息數據,然后按照每1000條數據為一組,讀取到內存里進行處理,以此類推,直到查詢sql匹配到數據全部處理完,再提交事務,關閉sqlSession;

@Service
@Slf4j
public class PersonServiceImpl implements IPersonService {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    @Override
    public void getOneByAsync() throws InterruptedException {
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                //使用sqlSessionFactory打開一個sqlSession,在沒有讀取完數據之前不要提交事務或關閉sqlSession
                log.info("----開啟sqlSession");
                SqlSession sqlSession = sqlSessionFactory.openSession();
                 try {
                     //獲取到指定mapper
                     PersonDao mapper = sqlSession.getMapper(PersonDao.class);
                     //調用指定mapper的方法,返回一個cursor
                     Cursor<Person> cursor = mapper.selectByCursor();
                     //查詢數據總量
                     Integer total = mapper.queryCount();
                     //定義一個list,用來從cursor中讀取數據,每讀取夠1000條的時候,開始處理這批數據;
                     //當前批數據處理完之后,清空list,準備接收下一批次數據;直到大量的數據全部處理完;
                     List<Person> personList = new ArrayList<>();
                     int i = 0;
                     if (cursor != null) {
                         for (Person person : cursor) {
                             if (personList.size() < 1000) {
//                            log.info("----id:{},userName:{}", person.getId(), person.getUserName());
                                 personList.add(person);
                             } else if (personList.size() == 1000) {
                                 ++i;
                                 log.info("----{}、從cursor取數據達到1000條,開始處理數據", i);
                                 log.info("----處理數據中...");
                                 Thread.sleep(1000);//休眠1s模擬處理數據需要消耗的時間;
                                 log.info("----{}、從cursor中取出的1000條數據已經處理完畢", i);
                                 personList.clear();
                                 personList.add(person);
                             }
                             if (total == (cursor.getCurrentIndex() + 1)) {
                                 ++i;
                                 log.info("----{}、從cursor取數據達到1000條,開始處理數據", i);
                                 log.info("----處理數據中...");
                                 Thread.sleep(1000);//休眠1s模擬處理數據需要消耗的時間;
                                 log.info("----{}、從cursor中取出的1000條數據已經處理完畢", i);
                                 personList.clear();
                             }
                         }
                         if (cursor.isConsumed()) {
                             log.info("----查詢sql匹配中的數據已經消費完畢!");
                         }
                     }
                     sqlSession.commit();
                     log.info("----提交事務");
                 }catch (Exception e){
                     e.printStackTrace();
                     sqlSession.rollback();
                 }
                 finally {
                     if (sqlSession != null) {
                         //全部數據讀取并且做好其他業(yè)務操作之后,提交事務并關閉連接;
                         sqlSession.close();
                         log.info("----關閉sqlSession");  
                     }
                 }
            }
        }).start();
    }
}

應用場景

其實mybatis的流式查詢適用范圍很有限,這里舉個例子,假如有這樣一個需求 :有50萬員工的一年的工資數據明細,需要輸出一張公司支出工資的數據報表。

需求很簡單,估計有人是這樣想:這太簡單了,查詢出員工的工資數據明細,然后按照套上公式逐條計算出結果,然后匯總計算結果,插入到新的結果表里不就行了。事實上這件事絕對不簡單:

  • 50萬的數據全部讀取到jvm的內存里得占用多大空間?

  • 這么多對象的垃圾回收又需要多久?

  • 這么多數據計算是高頻行為還是低步行為?

  • 如果計算到某條員工的數據發(fā)生異常,已經計算好的數據要不要全部回滾?...

總之,直接取出50萬數據來計算,風險肯定不小。那怎么辦呢?

在實際的開發(fā)中,也經常遇到一些百十萬,說大不大,說小不小的數據報表處理,我的主要設計思路通常就是數據切隔+異步,具體怎么做呢?結合上面的例子,是這樣的:

1、按照月份、省份或者部門,對工資明細數據進行數據切隔分組;

2、把不同月份、省份、部門的工資數據包裝成多線程任務,放到線程池中去執(zhí)行;

3、根據切隔的多線程任務數量,定義一個同步工具類CountDownLatch;

4、根據同步工具類CountDownLatch,來判斷所有的多線程任務是否全部執(zhí)行完;等到所有的多線程任務全部執(zhí)行完成后,再執(zhí)行匯總的邏輯;

5、在多線程任務里,查詢具體月份、省份的員工工資數據明細的時候,如果數據量還是不少,就可以使用mybatis的流式查詢,分批獲取員工工資明細數據,進行當前批的計算、匯總,然后所有分批數據都計算完成后,再匯總所有分批數據;

注意事項

mybatis的流式查詢的本意,是避免大量數據的查詢而導致內存溢出,因此dao層查詢返回的是一個迭代器(Cursor),可以每次從迭代器中取出一條查詢結果,在實際業(yè)務開發(fā)過程中,即是根據實際的jvm內存大小,從迭代器中取出一定數量的數據后,再進行數據處理,待處理完之后,繼續(xù)取出一定數據再處理,以此類推直到全部數據處理完,這樣做的最大好處就是能夠降低內存使用和垃圾回收器的負擔,使數據處理的過程相對更加高效、可控,內存溢出的風險較小;

好處很明顯,缺點也很就明顯,處理的時間可能會變長,需要引入多線程異步操作,并且在迭代器遍歷和數據處理的過程中,數據庫連接不能斷開,即當前sqlSession要保持持續(xù)打開狀態(tài),一量斷開,數據讀取就會中斷,所以關于這塊的處理,使用mybatis原生的sqlSession進行手動查詢、提交事務、回滾和關閉sqlSession最為穩(wěn)妥、最簡單

以上就是SpringBoot項目實現(xiàn)MyBatis流式查詢的教程詳解的詳細內容,更多關于SpringBoot MyBatis流式查詢的資料請關注腳本之家其它相關文章!

相關文章

  • Java Spring中Bean配置核心規(guī)則id、name、ref的用法與區(qū)別解析

    Java Spring中Bean配置核心規(guī)則id、name、ref的用法與區(qū)別解析

    本文詳解Spring XML中Bean配置的id、name、ref屬性用法與區(qū)別,涵蓋唯一性、命名規(guī)范及依賴引用規(guī)則,分析常見錯誤,幫助開發(fā)者正確配置Bean并掌握依賴注入邏輯,為整合其他框架打下基礎,感興趣的朋友一起看看吧
    2025-07-07
  • SpringBoot2.6.x升級后循環(huán)依賴及Swagger無法使用問題

    SpringBoot2.6.x升級后循環(huán)依賴及Swagger無法使用問題

    這篇文章主要為大家介紹了SpringBoot2.6.x升級后循環(huán)依賴及Swagger無法使用問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 從Mybatis-Plus開始認識SerializedLambda的詳細過程

    從Mybatis-Plus開始認識SerializedLambda的詳細過程

    這篇文章主要介紹了從Mybatis-Plus開始認識SerializedLambda,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2024-07-07
  • Java圖片讀取ImageIO.read()報錯問題及解決

    Java圖片讀取ImageIO.read()報錯問題及解決

    在使用imageio庫讀取圖片時,如果路徑中包含中文,可能會導致讀取失敗,解決方法是將路徑中的中文字符進行轉義處理,可以使用ImageUtil.java工具類進行路徑轉義,從而避免錯誤,這是一個常見問題,希望本文的解決方案能幫助到遇到相同問題的開發(fā)者
    2024-10-10
  • 使用spring?security?BCryptPasswordEncoder接入系統(tǒng)

    使用spring?security?BCryptPasswordEncoder接入系統(tǒng)

    這篇文章主要介紹了使用spring?security?BCryptPasswordEncoder接入系統(tǒng)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Java設計模式之創(chuàng)建者模式簡介

    Java設計模式之創(chuàng)建者模式簡介

    這篇文章主要介紹了Java設計模式之創(chuàng)建者模式,需要的朋友可以參考下
    2014-07-07
  • Java編程訪問權限的控制代碼詳解

    Java編程訪問權限的控制代碼詳解

    這篇文章主要介紹了Java編程訪問權限的控制代碼詳解,涉及包名,公共的和私有的等相關內容,分享了相關代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • Java synchronized同步方法詳解

    Java synchronized同步方法詳解

    這篇文章主要為大家詳細介紹了Java synchronized同步方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • Java中的ReentrantLock實現(xiàn)原理及代碼演示

    Java中的ReentrantLock實現(xiàn)原理及代碼演示

    這篇文章主要介紹了Java中的ReentrantLock實現(xiàn)原理及代碼演示,非公平鎖 如果已經進入隊列,鏈表里面的線程是先進先出,如果已經釋放了鎖,在搶占鎖時,鏈表里面的頭結點和還沒有入隊列的線程搶鎖,需要的朋友可以參考下
    2024-01-01
  • PowerJob的GridFsManager工作流程源碼解讀

    PowerJob的GridFsManager工作流程源碼解讀

    這篇文章主要為大家介紹了PowerJob的GridFsManager工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01

最新評論

东安县| 哈密市| 平度市| 永靖县| 仙桃市| 饶河县| 普安县| 廉江市| 陵水| 林西县| 霍山县| 南投县| 祁门县| 威海市| 张家界市| 平度市| 宁晋县| 台湾省| 石门县| 磐安县| 图木舒克市| 横峰县| 都匀市| 金坛市| 阳谷县| 嵊州市| 镇安县| 漳平市| 呈贡县| 防城港市| 新丰县| 武鸣县| 鞍山市| 长岭县| 翁牛特旗| 濮阳市| 德江县| 重庆市| 镇江市| 扎兰屯市| 宿州市|