0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MyBatis流式查詢輕松幫你解決分頁(yè)慢的問(wèn)題

5jek_harmonyos ? 來(lái)源:思否開(kāi)發(fā)者社區(qū) ? 作者:捏造的信仰 ? 2021-08-04 15:52 ? 次閱讀

作者丨捏造的信仰

segmentfault.com/a/1190000022478915

Part1基本概念

流式查詢指的是查詢成功后不是返回一個(gè)集合而是返回一個(gè)迭代器,應(yīng)用每次從迭代器取一條查詢結(jié)果。流式查詢的好處是能夠降低內(nèi)存使用。

如果沒(méi)有流式查詢,我們想要從數(shù)據(jù)庫(kù)取 1000 萬(wàn)條記錄而又沒(méi)有足夠的內(nèi)存時(shí),就不得不分頁(yè)查詢,而分頁(yè)查詢效率取決于表設(shè)計(jì),如果設(shè)計(jì)的不好,就無(wú)法執(zhí)行高效的分頁(yè)查詢。因此流式查詢是一個(gè)數(shù)據(jù)庫(kù)訪問(wèn)框架必須具備的功能。

流式查詢的過(guò)程當(dāng)中,數(shù)據(jù)庫(kù)連接是保持打開(kāi)狀態(tài)的,因此要注意的是:執(zhí)行一個(gè)流式查詢后,數(shù)據(jù)庫(kù)訪問(wèn)框架就不負(fù)責(zé)關(guān)閉數(shù)據(jù)庫(kù)連接了,需要應(yīng)用在取完數(shù)據(jù)后自己關(guān)閉。

Part2MyBatis 流式查詢接口

MyBatis 提供了一個(gè)叫 org.apache.ibatis.cursor.Cursor 的接口類用于流式查詢,這個(gè)接口繼承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知:

Cursor 是可關(guān)閉的。實(shí)際上當(dāng)關(guān)閉 Cursor 時(shí),也一并將數(shù)據(jù)庫(kù)連接關(guān)閉了;

Cursor 是可遍歷的。

除此之外,Cursor 還提供了三個(gè)方法:

isOpen():用于在取數(shù)據(jù)之前判斷 Cursor 對(duì)象是否是打開(kāi)狀態(tài)。只有當(dāng)打開(kāi)時(shí) Cursor 才能取數(shù)據(jù);

isConsumed():用于判斷查詢結(jié)果是否全部取完;

getCurrentIndex():返回已經(jīng)獲取了多少條數(shù)據(jù)。

因?yàn)?Cursor 實(shí)現(xiàn)了迭代器接口,因此在實(shí)際使用當(dāng)中,從 Cursor 取數(shù)據(jù)非常簡(jiǎn)單:

try(Cursor cursor = mapper.querySomeData()) {

cursor.forEach(rowObject -》 {

// 。。。

});

}

使用 try-resource 方式可以令 Cursor 自動(dòng)關(guān)閉。

Part3但構(gòu)建 Cursor 的過(guò)程不簡(jiǎn)單

我們舉個(gè)實(shí)際例子。下面是一個(gè) Mapper 類:

@Mapper

public interface FooMapper {

@Select(“select * from foo limit #{limit}”)

Cursor《Foo》 scan(@Param(“l(fā)imit”) int limit);

}

方法 scan() 是一個(gè)非常簡(jiǎn)單的查詢。我們?cè)诙x這個(gè)方時(shí),指定返回值為 Cursor 類型,MyBatis 就明白這個(gè)查詢方法是一個(gè)流式查詢。

然后我們?cè)賹懸粋€(gè) SpringMVC Controller 方法來(lái)調(diào)用 Mapper(無(wú)關(guān)的代碼已經(jīng)省略):

@GetMapping(“foo/scan/0/{limit}”)

public void scanFoo0(@PathVariable(“l(fā)imit”) int limit) throws Exception {

try (Cursor《Foo》 cursor = fooMapper.scan(limit)) { // 1

cursor.forEach(foo -》 {}); // 2

}

}

假設(shè) fooMapper 是 @Autowired 進(jìn)來(lái)的。注釋 1 處是獲取 Cursor 對(duì)象并保證它能最后關(guān)閉;2 處則是從 cursor 中取數(shù)據(jù)。

上面的代碼看上去沒(méi)什么問(wèn)題,但是執(zhí)行 scanFoo0(int) 時(shí)會(huì)報(bào)錯(cuò):

java.lang.IllegalStateException: A Cursor is already closed.

這是因?yàn)槲覀兦懊嬲f(shuō)了在取數(shù)據(jù)的過(guò)程中需要保持?jǐn)?shù)據(jù)庫(kù)連接,而 Mapper 方法通常在執(zhí)行完后連接就關(guān)閉了,因此 Cusor 也一并關(guān)閉了。

所以,解決這個(gè)問(wèn)題的思路不復(fù)雜,保持?jǐn)?shù)據(jù)庫(kù)連接打開(kāi)即可。我們至少有三種方案可選。

方案一:SqlSessionFactory

我們可以用 SqlSessionFactory 來(lái)手工打開(kāi)數(shù)據(jù)庫(kù)連接,將 Controller 方法修改如下:

@GetMapping(“foo/scan/1/{limit}”)

public void scanFoo1(@PathVariable(“l(fā)imit”) int limit) throws Exception {

try (

SqlSession sqlSession = sqlSessionFactory.openSession(); // 1

Cursor《Foo》 cursor =

sqlSession.getMapper(FooMapper.class).scan(limit) // 2

) {

cursor.forEach(foo -》 { });

}

}

上面的代碼中,1 處我們開(kāi)啟了一個(gè) SqlSession (實(shí)際上也代表了一個(gè)數(shù)據(jù)庫(kù)連接),并保證它最后能關(guān)閉;2 處我們使用 SqlSession 來(lái)獲得 Mapper 對(duì)象。這樣才能保證得到的 Cursor 對(duì)象是打開(kāi)狀態(tài)的。

方案二:TransactionTemplate

在 Spring 中,我們可以用 TransactionTemplate 來(lái)執(zhí)行一個(gè)數(shù)據(jù)庫(kù)事務(wù),這個(gè)過(guò)程中數(shù)據(jù)庫(kù)連接同樣是打開(kāi)的。代碼如下:

@GetMapping(“foo/scan/2/{limit}”)

public void scanFoo2(@PathVariable(“l(fā)imit”) int limit) throws Exception {

TransactionTemplate transactionTemplate =

new TransactionTemplate(transactionManager); // 1

transactionTemplate.execute(status -》 { // 2

try (Cursor《Foo》 cursor = fooMapper.scan(limit)) {

cursor.forEach(foo -》 { });

} catch (IOException e) {

e.printStackTrace();

}

return null;

});

}

上面的代碼中,1 處我們創(chuàng)建了一個(gè) TransactionTemplate 對(duì)象(此處 transactionManager 是怎么來(lái)的不用多解釋,本文假設(shè)讀者對(duì) Spring 數(shù)據(jù)庫(kù)事務(wù)的使用比較熟悉了),2 處執(zhí)行數(shù)據(jù)庫(kù)事務(wù),而數(shù)據(jù)庫(kù)事務(wù)的內(nèi)容則是調(diào)用 Mapper 對(duì)象的流式查詢。注意這里的 Mapper 對(duì)象無(wú)需通過(guò) SqlSession 創(chuàng)建。

方案三:@Transactional 注解

這個(gè)本質(zhì)上和方案二一樣,代碼如下:

@GetMapping(“foo/scan/3/{limit}”)

@Transactional

public void scanFoo3(@PathVariable(“l(fā)imit”) int limit) throws Exception {

try (Cursor《Foo》 cursor = fooMapper.scan(limit)) {

cursor.forEach(foo -》 { });

}

}

它僅僅是在原來(lái)方法上面加了個(gè) @Transactional 注解。這個(gè)方案看上去最簡(jiǎn)潔,但請(qǐng)注意 Spring 框架當(dāng)中注解使用的坑:只在外部調(diào)用時(shí)生效。在當(dāng)前類中調(diào)用這個(gè)方法,依舊會(huì)報(bào)錯(cuò)。

以上是三種實(shí)現(xiàn) MyBatis 流式查詢的方法。

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • mybatis
    +關(guān)注

    關(guān)注

    0

    文章

    58

    瀏覽量

    6693

原文標(biāo)題:還在擔(dān)心分頁(yè)慢嗎? MyBatis 流式查詢解決你的煩惱

文章出處:【微信號(hào):harmonyos_developer,微信公眾號(hào):harmonyos_developer】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    流式滑動(dòng)變阻器的選型原則

    流式滑動(dòng)變阻器,又稱為限流電阻器或限流電位器,是一種用于限制電路中電流大小的電子元件。在電子電路設(shè)計(jì)中,選擇合適的限流式滑動(dòng)變阻器對(duì)于確保電路的穩(wěn)定運(yùn)行和提高電路性能至關(guān)重要。 一、限流式滑動(dòng)
    的頭像 發(fā)表于 08-05 14:31 ?558次閱讀

    使用mybatis切片實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制

    一、使用方式 數(shù)據(jù)權(quán)限控制需要對(duì)查詢出的數(shù)據(jù)進(jìn)行篩選,對(duì)業(yè)務(wù)入侵最少的方式就是利用mybatis或者數(shù)據(jù)庫(kù)連接池的切片對(duì)已有業(yè)務(wù)的sql進(jìn)行修改。切片邏輯完成后,僅需要在業(yè)務(wù)中加入少量標(biāo)記代碼
    的頭像 發(fā)表于 07-09 17:26 ?294次閱讀
    使用<b class='flag-5'>mybatis</b>切片實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制

    分庫(kù)分表后復(fù)雜查詢的應(yīng)對(duì)之道:基于DTS實(shí)時(shí)性ES寬表構(gòu)建技術(shù)實(shí)踐

    分表,通過(guò)分庫(kù)分表應(yīng)對(duì)存系統(tǒng)讀寫性能瓶頸和存儲(chǔ)瓶頸;分庫(kù)分表幫我們解決問(wèn)題的同時(shí),也帶來(lái)了復(fù)雜性;比如多條件的分頁(yè)查詢,多條件的聯(lián)表查詢變得復(fù)雜起來(lái),通過(guò)調(diào)研我們發(fā)現(xiàn)針對(duì)這些分頁(yè),聯(lián)表
    的頭像 發(fā)表于 06-25 18:30 ?786次閱讀
    分庫(kù)分表后復(fù)雜<b class='flag-5'>查詢</b>的應(yīng)對(duì)之道:基于DTS實(shí)時(shí)性ES寬表構(gòu)建技術(shù)實(shí)踐

    讓你的年終總結(jié)更有格調(diào)!訊飛星火AI商務(wù)鍵盤幫你輕松撰寫

    親愛(ài)的朋友們,年底了,是不是已經(jīng)開(kāi)始忙碌起來(lái)準(zhǔn)備年終總結(jié)了呢?別擔(dān)心,我們有科大訊飛AI智能鍵盤D1來(lái)幫你輕松撰寫! 首先,讓我來(lái)為大家介紹一下這款神奇的鍵盤。它擁有訊飛星火認(rèn)知大模型,這可是我們
    的頭像 發(fā)表于 01-05 15:46 ?485次閱讀
    讓你的年終總結(jié)更有格調(diào)!訊飛星火AI商務(wù)鍵盤<b class='flag-5'>幫你</b><b class='flag-5'>輕松</b>撰寫

    mybatis映射文件的主要元素及作用

    MyBatis是一種流行的持久層框架,它提供了一種簡(jiǎn)單的方式來(lái)映射關(guān)系型數(shù)據(jù)庫(kù)和Java對(duì)象之間的關(guān)聯(lián),通過(guò)XML配置文件來(lái)描述數(shù)據(jù)庫(kù)表和Java類之間的映射關(guān)系。在MyBatis的映射文件中,包含
    的頭像 發(fā)表于 12-03 14:56 ?2608次閱讀

    mybatis邏輯分頁(yè)和物理分頁(yè)的區(qū)別

    MyBatis是一個(gè)開(kāi)源的Java持久層框架,它與其他ORM(對(duì)象關(guān)系映射)框架相比,具有更加靈活和高性能的特點(diǎn)。MyBatis提供了兩種分頁(yè)方式,即邏輯分頁(yè)和物理
    的頭像 發(fā)表于 12-03 14:54 ?783次閱讀

    mybatis中$和井號(hào)區(qū)別

    MyBatis是一個(gè)開(kāi)源的Java持久層框架,它提供了許多強(qiáng)大的功能用于簡(jiǎn)化數(shù)據(jù)庫(kù)操作。在MyBatis中,我們可以使用兩種方式來(lái)動(dòng)態(tài)生成SQL語(yǔ)句:$和#。 和#都可以用來(lái)替換SQL語(yǔ)句中的參數(shù)
    的頭像 發(fā)表于 12-03 14:53 ?886次閱讀

    mybatis框架的主要作用

    。MyBatis框架的主要作用包括以下幾個(gè)方面。 數(shù)據(jù)庫(kù)操作的簡(jiǎn)化和標(biāo)準(zhǔn)化: MyBatis框架提供了一種簡(jiǎn)單的方式來(lái)執(zhí)行數(shù)據(jù)庫(kù)操作,包括插入、更新、刪除和查詢等操作。通過(guò)使用MyBatis
    的頭像 發(fā)表于 12-03 14:49 ?1891次閱讀

    mybatis一級(jí)緩存和二級(jí)緩存的原理

    SqlSession的生命周期中,當(dāng)SqlSession關(guān)閉時(shí),一級(jí)緩存也會(huì)被清空。 1.2 緩存實(shí)現(xiàn)機(jī)制 一級(jí)緩存采用了基于PerpetualCache的HashMap來(lái)實(shí)現(xiàn),使用一個(gè)Map對(duì)象來(lái)保存緩存的數(shù)據(jù)。當(dāng)執(zhí)行相同的查詢時(shí),MyBatis會(huì)首先尋找一級(jí)緩存中是否
    的頭像 發(fā)表于 12-03 11:55 ?1005次閱讀

    mybatis和mybatisplus的區(qū)別

    MyBatisMyBatis Plus是兩個(gè)非常受歡迎的Java持久層框架。這兩個(gè)框架在設(shè)計(jì)和功能上有一些區(qū)別,下面我將詳細(xì)介紹它們之間的差異以及各自的特點(diǎn)。 設(shè)計(jì)理念與目標(biāo): MyBatis是一
    的頭像 發(fā)表于 12-03 11:53 ?2388次閱讀

    mybatis接口動(dòng)態(tài)代理原理

    MyBatis是一款輕量級(jí)的Java持久化框架,它通過(guò)XML或注解配置的方式,將數(shù)據(jù)庫(kù)操作與SQL語(yǔ)句解耦,提供了一種簡(jiǎn)單、靈活的數(shù)據(jù)訪問(wèn)方式。在MyBatis中,使用動(dòng)態(tài)代理技術(shù)來(lái)實(shí)現(xiàn)接口的代理
    的頭像 發(fā)表于 12-03 11:52 ?839次閱讀

    mybatis的dao能重載嗎

    MyBatis的DAO能否重載? 在MyBatis中,DAO是數(shù)據(jù)訪問(wèn)對(duì)象的縮寫,用于執(zhí)行與數(shù)據(jù)庫(kù)交互的操作。MyBatis的DAO可以重載,即可以定義多個(gè)具有不同參數(shù)的相同方法名的方法,以滿足
    的頭像 發(fā)表于 12-03 11:51 ?1184次閱讀

    Redis的分頁(yè)+多條件模糊查詢組合實(shí)現(xiàn)方案

    Redis是key-value類型的內(nèi)存數(shù)據(jù)庫(kù),通過(guò)key直接取數(shù)據(jù)雖然很方便,但是并未提供像mysql那樣方便的sql條件查詢支持。因此我們需要借助Redis提供的結(jié)構(gòu)和功能去自己實(shí)現(xiàn)模糊條件查詢功能。
    的頭像 發(fā)表于 11-20 14:26 ?815次閱讀
    Redis的<b class='flag-5'>分頁(yè)</b>+多條件模糊<b class='flag-5'>查詢</b>組合實(shí)現(xiàn)方案

    深入分析SQL的排查、解決思路

    出于一些歷史原因有的SQL查詢可能非常復(fù)雜,需要同時(shí)關(guān)聯(lián)非常多的表,使用一些復(fù)雜的函數(shù)、子查詢,這樣的SQL在項(xiàng)目初期由于數(shù)據(jù)量比較少,不會(huì)對(duì)數(shù)據(jù)庫(kù)造成較大的壓力,但是隨著時(shí)間的積累以及業(yè)務(wù)的發(fā)展,這些SQL慢慢就會(huì)轉(zhuǎn)變?yōu)?b class='flag-5'>慢SQ
    的頭像 發(fā)表于 10-31 10:29 ?1601次閱讀
    深入分析<b class='flag-5'>慢</b>SQL的排查、解決思路

    看完本篇,幫你糾正錯(cuò)誤的去耦方法

    看完本篇,幫你糾正錯(cuò)誤的去耦方法
    的頭像 發(fā)表于 10-26 15:22 ?412次閱讀
    看完本篇,<b class='flag-5'>幫你</b>糾正錯(cuò)誤的去耦方法