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

Mybatis流式查詢之ResultHanlde問題

 更新時(shí)間:2023年08月11日 09:47:56   作者:認(rèn)真的老去  
這篇文章主要介紹了Mybatis流式查詢之ResultHanlde問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

前言

正常訪問數(shù)據(jù)庫的查詢操作,都是根據(jù)查詢sql一次性返回查詢結(jié)果。

但如果遇到目標(biāo)數(shù)據(jù)量過大、且需要全量查詢、不能分頁、或者內(nèi)存不想被返回的結(jié)果占用過多等需求時(shí)(例如導(dǎo)出excel),就可能需要流式查詢。

1.準(zhǔn)備工作

1.1.Mybaits的jar包引入

注:idea必須配置build,否則掃描不到src下的xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>relife</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>relife-object</artifactId>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

1.2.實(shí)體類User

public class User {
    private String name;
    private int age;
    private String id;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}

1.3.mybatis-config.xml和userMapper.xml

mybatis-config.xml(resource根目錄)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/relife/mybatis/userMapper.xml"/>
    </mappers>
</configuration>

userMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.relife.object.User">
    <select id="selectUser" resultType="com.relife.object.User">
		select id,name,age from user
	</select>
</mapper>

2.流式處理

2.1流式逐條處理handle

public class UserResultHandler implements ResultHandler<User> {
    @Override
    public void handleResult(ResultContext<? extends User> resultContext) {
        // 這里獲取流式查詢每次返回的單條結(jié)果
        User user = resultContext.getResultObject();
        handle(user);
    }
   // 串行逐條執(zhí)行handle
    private void handle(User user) {
        System.out.println(user.getId());
    }
}

適用于導(dǎo)出excel等,,上述寫法缺點(diǎn)也很明顯,單線程,串行,所有效率慢,但是類似導(dǎo)出excel,符合要求,也不太方便使用多線程。

2.2.流式批量多線程處理handle

為了解決效率問題,有時(shí)候會(huì)對(duì)結(jié)果有比如發(fā)送請(qǐng)求,更行其他內(nèi)容等需求時(shí)

public class UserResultHandler<T> implements ResultHandler<T> {
    public final Logger logger = Logger.getLogger(this.getClass());
    /**
     * 線程池線程數(shù)
     */
    private int threadPollNum = 100;
    public UserResultHandler() {
    }
    public UserResultHandler(int threadPollNum) {
        this.threadPollNum = threadPollNum;
    }
    // 線程池
    public ExecutorService executorService = Executors.newFixedThreadPool(threadPollNum);
    // 線程執(zhí)行結(jié)果
    public List<Future> futureList = new ArrayList<>();
    @Override
    public void handleResult(ResultContext<? extends T> resultContext) {
        // 這里獲取流式查詢每次返回的單條結(jié)果
        T user = resultContext.getResultObject();
        while (futureList.size() > 200) {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            futureList = futureList.stream().filter(future -> !future.isDone()).collect(Collectors.toList());
            logger.info("條數(shù):" + resultContext.getResultCount() + "->未完成結(jié)果" + futureList.size());
        }
        UserThread ut = new UserThread(user);
        Future<?> future = executorService.submit(ut);
        futureList.add(future);
    }
    /**
     * 保證所有線程執(zhí)行完成,并關(guān)閉線程池
     */
    public void end() {
        while (futureList.size() != 0) {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            futureList = futureList.stream().filter(future -> !future.isDone()).collect(Collectors.toList());
        }
        executorService.shutdown();
    }
    public static class UserThread<T> implements Runnable {
        private T obejct;
        public UserThread(T obejct) {
            this.obejct = obejct;
        }
        @Override
        public void run() {
            System.out.println(((User)obejct).getId());
        }
        public T getObejct() {
            return obejct;
        }
        public void setObejct(T obejct) {
            this.obejct = obejct;
        }
    }
}

上述寫法,可用于需要線程返回值的,或者明確需要線程執(zhí)行完成,且可以保證不占用過多內(nèi)存。

其中Thread.sleep()方法,可以自定義時(shí)間。

3.測(cè)試

3.1.測(cè)試案例

public static void main(String[] args) throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        System.out.println("sqlSessionFactory:" + sqlSessionFactory);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 正常查詢
        List<User> userList5 = sqlSession.selectList("selectUser");
        // 流式查詢
        UserResultHandler userResultHandler = new UserResultHandler(100);
        sqlSession.select("selectUser", userResultHandler);
        userResultHandler.end();
        sqlSession.close();
    }

3.1.正常查詢debug調(diào)用

調(diào)用圖

在這里插入圖片描述

3.2.流式查詢debug調(diào)用

調(diào)用圖

在這里插入圖片描述

4.問題與部分源碼解析

4.1加入resulthandle為什么會(huì)流式查詢?

在DefaultResultSetHandler類中handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults, ResultMapping parentMapping) throws SQLException 方法中出現(xiàn)了調(diào)用區(qū)別一個(gè)303行,一個(gè)206行

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
      	// 有的話就使用傳入的ResultHandle,沒有就用默認(rèn)的DefaultResultHandler
        if (resultHandler == null) {
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          // 指定handle時(shí),把結(jié)果集放到multipleResults中
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          // 非指定handle時(shí),multipleResults中不會(huì)有結(jié)果
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }

這個(gè)if判斷直接導(dǎo)致了最后實(shí)際調(diào)用callResultHandler時(shí)的區(qū)別。

  private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
    resultContext.nextResultObject(rowValue);
    // 加入handle的會(huì)調(diào)用自己的代碼實(shí)現(xiàn),沒有就用默認(rèn)的DefaultResultHandler里的方法
    ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
  }

4.2.流式查詢?yōu)槭裁礇]有返回值?

這個(gè)可以從兩個(gè)方向探討

  • 一個(gè)是比較直接的,即什么時(shí)候返回值變成了void,見DefaultSqlSession
  • 一個(gè)是為什么會(huì)這樣,因?yàn)樽远x實(shí)現(xiàn)是,沒有把結(jié)果加入結(jié)果集,見DefaultResultSetHandler

1.DefaultSqlSession

 // 正常查詢
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
 // 流式查詢
 @Override
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 此方法有返回結(jié)果,但是為空,不需要返回(見下邊方法)
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

2.DefaultResultSetHandler

@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
	// 初始化返回值
    final List<Object> multipleResults = new ArrayList<Object>();
    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 返回值會(huì)在調(diào)用方法時(shí),被賦值,從上述4.1可看出,只有不傳值時(shí)才會(huì)把結(jié)果放入multipleResults
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
    return collapseSingleResultList(multipleResults);
  }
  // 只有一條結(jié)果,就直接取出,多個(gè)或空,不處理
  private List<Object> collapseSingleResultList(List<Object> multipleResults) {
    return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
  }

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springboot部署linux訪問服務(wù)器資源的方法

    springboot部署linux訪問服務(wù)器資源的方法

    這篇文章主要介紹了springboot部署linux訪問服務(wù)器資源,部署springboot項(xiàng)目至服務(wù)器用了幾種不同方法,文中給大家詳細(xì)介紹,需要的朋友可以參考下
    2019-12-12
  • MyBatis批量插入數(shù)據(jù)到Oracle數(shù)據(jù)庫中的兩種方式(實(shí)例代碼)

    MyBatis批量插入數(shù)據(jù)到Oracle數(shù)據(jù)庫中的兩種方式(實(shí)例代碼)

    本文通過實(shí)例代碼給大家分享了MyBatis批量插入數(shù)據(jù)到Oracle數(shù)據(jù)庫中的兩種方式,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2017-09-09
  • 一篇文章帶你搞定JAVA注解

    一篇文章帶你搞定JAVA注解

    這篇文章主要介紹了詳解Java注解的實(shí)現(xiàn)與使用方法的相關(guān)資料,希望通過本文大家能夠理解掌握J(rèn)ava注解的知識(shí),需要的朋友可以參考下
    2021-07-07
  • Spring Security 實(shí)現(xiàn)“記住我”功能及原理解析

    Spring Security 實(shí)現(xiàn)“記住我”功能及原理解析

    這篇文章主要介紹了Spring Security 實(shí)現(xiàn)“記住我”功能及原理解析,需要的朋友可以參考下
    2020-05-05
  • RocketMQ-延遲消息的處理流程介紹

    RocketMQ-延遲消息的處理流程介紹

    這篇文章主要介紹了RocketMQ-延遲消息的處理流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 一文詳解Java中的finalize()方法

    一文詳解Java中的finalize()方法

    Java中的finalize()是Object類的一個(gè)方法,用于對(duì)象被垃圾回收前執(zhí)行清理操作,這篇文章主要介紹了Java中finalize()方法的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • java多線程三種上鎖方式小結(jié)

    java多線程三種上鎖方式小結(jié)

    本文主要介紹了java多線程三種上鎖方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • SpringBoot如何根據(jù)目錄結(jié)構(gòu)生成API接口前綴

    SpringBoot如何根據(jù)目錄結(jié)構(gòu)生成API接口前綴

    這篇文章主要介紹了SpringBoot如何根據(jù)目錄結(jié)構(gòu)生成API接口前綴,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 如何替換@PathVariable中的變量

    如何替換@PathVariable中的變量

    這篇文章主要介紹了如何替換@PathVariable中的變量,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Spring Boot創(chuàng)建非可執(zhí)行jar包的實(shí)例教程

    Spring Boot創(chuàng)建非可執(zhí)行jar包的實(shí)例教程

    這篇文章主要介紹了Spring Boot創(chuàng)建非可執(zhí)行jar包的實(shí)例教程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02

最新評(píng)論

汝州市| 无为县| 故城县| 齐齐哈尔市| 沙坪坝区| 北海市| 桐乡市| 旬阳县| 紫金县| 库车县| 清河县| 阳朔县| 宜章县| 钦州市| 彰化市| 合水县| 武夷山市| 冷水江市| 连州市| 高密市| 泰宁县| 合肥市| 东山县| 大厂| 巫山县| 德庆县| 鄂尔多斯市| 涿鹿县| 江川县| 镇平县| 黄浦区| 永昌县| 永吉县| 安宁市| 安徽省| 班戈县| 广平县| 谷城县| 平谷区| 勐海县| 思南县|