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

Mybatis的Cursor避免OOM異常的方法詳解

 更新時(shí)間:2024年06月27日 08:53:07   作者:Azir12138  
在Mybatis中,有一個(gè)特殊的對(duì)象Cursor,這個(gè)對(duì)象的注釋上清晰的說(shuō)明了,這個(gè)類的用途,在Mybatis中使用Cursor非常簡(jiǎn)單,只要在Mapper文件中將方法的返回值設(shè)置成Cursor<T>即可,本文給大家介紹了Mybatis的Cursor避免OOM異常的方法,需要的朋友可以參考下

Cursor是啥

研究Cursor如何避免OOM異常之前,先了解一下Cursor是啥。
在Mybatis中,有一個(gè)特殊的對(duì)象Cursor,這個(gè)對(duì)象的注釋上清晰的說(shuō)明了,這個(gè)類的用途。

/**
 * Cursor contract to handle fetching items lazily using an Iterator.
 * Cursors are a perfect fit to handle millions of items queries that would not normally fits in memory.
 * If you use collections in resultMaps then cursor SQL queries must be ordered (resultOrdered="true")
 * using the id columns of the resultMap.
 *
 * @author Guillaume Darmont / guillaume@dropinocean.com
 */

Cursors are a perfect fit to handle millions of items queries that would not normally fits in memory. Cursor非常適合處理通常不適合內(nèi)存的數(shù)百萬(wàn)項(xiàng)查詢

甚至在說(shuō)明中還著重的說(shuō)明了是非常適合的。
這個(gè)類的作用其實(shí)就是為了避免在數(shù)據(jù)庫(kù)批量查詢到大數(shù)據(jù)時(shí)導(dǎo)致程序OOM錯(cuò)誤。

如何使用Cursor

Mybatis中使用Cursor非常簡(jiǎn)單,只要在Mapper文件中將方法的返回值設(shè)置成Cursor<T>即可。

@Select("SELECT * FROM log")
Cursor<Log> selectAll();

注意:要是想在SpringBoot中使用Cursor的話,需要下面方式二選一,不然的話使用Cursor會(huì)報(bào)錯(cuò)。

  • 手動(dòng)創(chuàng)建SqlSession
  • 在調(diào)用Mapper方法的方法上標(biāo)注@Transactional事務(wù)注解。

之所以需要額外配置是因?yàn)樵赟pringBoot中,Mybatis的SqlSession生命周期只在Mapper方法中,并且在關(guān)閉SqlSession時(shí),還會(huì)將SqlSession**綁定的Cursor關(guān)閉,**所以就需要延長(zhǎng)SqlSession的存活時(shí)間了。

Cursor原理

解析Mapper方法返回值

在Mybatis中,調(diào)用Mapper方法時(shí),會(huì)由MapperProxy進(jìn)行方法的代理。此時(shí)就會(huì)根據(jù)具體的方法進(jìn)行不同的解析

public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
    // 解析方法返回值
    Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
    if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
    } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
    } else {
        this.returnType = method.getReturnType();
    }
    this.returnsVoid = void.class.equals(this.returnType);
    this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
    // 方法是否返回Cursor類型
    this.returnsCursor = Cursor.class.equals(this.returnType);
    this.returnsOptional = Optional.class.equals(this.returnType);
    this.mapKey = getMapKey(method);
    this.returnsMap = this.mapKey != null;
    this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
    this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
    this.paramNameResolver = new ParamNameResolver(configuration, method);
}

根據(jù)Cursor返回值調(diào)用selectCursor

解析Mapper方法得到返回值后,就會(huì)根據(jù)返回值的類型來(lái)決定具體調(diào)用的查詢方法。

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
    // ---------- 其他查詢----------------
        case SELECT:
            if (method.returnsVoid() && method.hasResultHandler()) {
                executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (method.returnsMany()) {
                result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {
                result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {
                // Cursor返回類型
                result = executeForCursor(sqlSession, args);
            } else {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
    // ---------- 其他查詢----------------
    return result;
}

構(gòu)建statement

使用上面解析Mapper方法后得到的Sql,從數(shù)據(jù)庫(kù)鏈接中創(chuàng)建一個(gè)PreparedStatement并填充對(duì)應(yīng)的參數(shù)值。

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}

封裝Cursor

在調(diào)用的最后,會(huì)將從數(shù)據(jù)庫(kù)得到的ResultSet以及Mybatis內(nèi)部ResultSetHandler封裝成Cursor對(duì)象供用戶使用。

public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());
    
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    if (resultMapCount != 1) {
        throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
    }
    
    ResultMap resultMap = resultMaps.get(0);
    return new DefaultCursor<>(this, resultMap, rsw, rowBounds);
}

為啥能避免內(nèi)存溢出

在討論這個(gè)問(wèn)題前,我們可以看一下在Mybatis中,Cursor返回值的查詢以及批量查詢的實(shí)際調(diào)用邏輯。

Cursor查詢

  @Override
  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
      throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    Cursor<E> cursor = handler.queryCursor(stmt);
    stmt.closeOnCompletion();
    return cursor;
  }

批量查詢

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
      BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
          boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

可以對(duì)比一下兩個(gè)實(shí)際執(zhí)行的方法,比較明顯的區(qū)別就是在批量搜索中,顯式關(guān)閉了打開的Statement,而在Cursor查詢中,并沒有關(guān)閉與數(shù)據(jù)庫(kù)的連接。歸根結(jié)底就是因?yàn)镃ursor在使用上就是在操作原生的Statement,故不能在查詢后關(guān)閉。
另外,在批量查詢的handler.query(stmt, resultHandler)方法中,是獲取本次查詢所有數(shù)據(jù)后返回的,而這就會(huì)導(dǎo)致在大批量數(shù)據(jù)時(shí)塞爆內(nèi)存導(dǎo)致OOM了。
然而在Cursor查詢中,并不會(huì)獲取全部數(shù)據(jù)后返回,而是根據(jù)用戶操作來(lái)獲取對(duì)于數(shù)據(jù),自然而然也就不會(huì)塞爆內(nèi)存了。

總結(jié)

類型如何獲取數(shù)據(jù)返回值是否關(guān)閉Statement、ResultSet
Cursor用戶自行根據(jù)Cursor迭代器獲取Cursor游標(biāo)類型不關(guān)閉,需要一直持有
普通搜索Mybatis內(nèi)部操控JDBC指針,獲取所有查詢數(shù)據(jù)后返回。具體實(shí)體類型獲取完數(shù)據(jù)后關(guān)閉

以上就是Mybatis的Cursor避免OOM異常的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Mybatis Cursor避免OOM異常的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Java冒泡排序

    詳解Java冒泡排序

    本篇文章通過(guò)代碼實(shí)例給大家詳細(xì)分析了Java冒泡排序的原理,有興趣的朋友可以學(xué)習(xí)下。
    2018-02-02
  • Java線程讓步y(tǒng)ield用法實(shí)例分析

    Java線程讓步y(tǒng)ield用法實(shí)例分析

    這篇文章主要介紹了Java線程讓步y(tǒng)ield用法,結(jié)合實(shí)例形式分析了java中yield()方法的功能、原理及線程讓步操作的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-09-09
  • springboot整合xxl-job的示例代碼

    springboot整合xxl-job的示例代碼

    這篇文章主要介紹了springboot整合xxl-job的示例代碼,主要分為三大模塊,分別是調(diào)度中心、執(zhí)行器和配置定時(shí)任務(wù)的過(guò)程,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • 教你如何在IDEA?中添加?Maven?項(xiàng)目的?Archetype(解決添加不起作用的問(wèn)題)

    教你如何在IDEA?中添加?Maven?項(xiàng)目的?Archetype(解決添加不起作用的問(wèn)題)

    這篇文章主要介紹了如何在?IDEA?中添加?Maven?項(xiàng)目的?Archetype(解決添加不起作用的問(wèn)題),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • Springboot分頁(yè)插件使用實(shí)例解析

    Springboot分頁(yè)插件使用實(shí)例解析

    這篇文章主要介紹了Springboot分頁(yè)插件使用實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • nacos實(shí)現(xiàn)配置多個(gè)配置文件(共享配置)

    nacos實(shí)現(xiàn)配置多個(gè)配置文件(共享配置)

    文章介紹了在Spring Cloud Nacos中配置共享配置文件和擴(kuò)展配置文件的方法,包括使用`shared-configs`和`extension-configs`屬性,并詳細(xì)解釋了這些屬性的使用方式和區(qū)別,文章還提到舊版本的配置方式已經(jīng)被棄用,建議使用新的配置方式
    2026-03-03
  • Java開發(fā)完整短信驗(yàn)證碼功能的全過(guò)程

    Java開發(fā)完整短信驗(yàn)證碼功能的全過(guò)程

    利用短信驗(yàn)證碼進(jìn)行身份驗(yàn)證是目前互聯(lián)網(wǎng)眾多產(chǎn)品常用的一種方式,那么這種短信驗(yàn)證功能是如何實(shí)現(xiàn)的呢,下面這篇文章主要給大家介紹了關(guān)于Java開發(fā)完整短信驗(yàn)證碼功能的相關(guān)資料,需要的朋友可以參考下
    2021-10-10
  • 項(xiàng)目為什么引入log4j而不是logback代碼

    項(xiàng)目為什么引入log4j而不是logback代碼

    這篇文章主要介紹了項(xiàng)目為什么引入log4j而不是logback代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Mybatis使用連表查詢的操作代碼

    Mybatis使用連表查詢的操作代碼

    這篇文章主要介紹了Mybatis如何使用連表查詢,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • SpringBoot項(xiàng)目啟動(dòng)錯(cuò)誤:找不到或無(wú)法加載主類的三種解決方法

    SpringBoot項(xiàng)目啟動(dòng)錯(cuò)誤:找不到或無(wú)法加載主類的三種解決方法

    在開發(fā)SpringBoot應(yīng)用時(shí),經(jīng)??赡軙?huì)遇到一個(gè)啟動(dòng)錯(cuò)誤:“錯(cuò)誤:找不到或無(wú)法加載主類 com.example.controller.demo.DemoApplication”,本文將介紹三種解決這一問(wèn)題的方法,需要的朋友可以參考下
    2024-10-10

最新評(píng)論

乐东| 清徐县| 广汉市| 桦南县| 广南县| 牟定县| 合水县| 凤城市| 安顺市| 壤塘县| 成都市| 昌江| 平原县| 株洲县| 怀集县| 琼海市| 永定县| 临江市| 望奎县| 威海市| 佛学| 长岭县| 通化市| 西充县| 汕尾市| 克拉玛依市| 富源县| 陈巴尔虎旗| 芷江| 怀远县| 临澧县| 华宁县| 松溪县| 贵溪市| 桃园县| 盘山县| 龙州县| 公主岭市| 南安市| 上虞市| 闽侯县|