欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis流式查詢之ResultHanlde問題

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

前言

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

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

1.準備工作

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.實體類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等,,上述寫法缺點也很明顯,單線程,串行,所有效率慢,但是類似導(dǎo)出excel,符合要求,也不太方便使用多線程。

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

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

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()方法,可以自定義時間。

3.測試

3.1.測試案例

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為什么會流式查詢?

在DefaultResultSetHandler類中handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults, ResultMapping parentMapping) throws SQLException 方法中出現(xiàn)了調(diào)用區(qū)別一個303行,一個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,沒有就用默認的DefaultResultHandler
        if (resultHandler == null) {
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          // 指定handle時,把結(jié)果集放到multipleResults中
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          // 非指定handle時,multipleResults中不會有結(jié)果
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }

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

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

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

這個可以從兩個方向探討

  • 一個是比較直接的,即什么時候返回值變成了void,見DefaultSqlSession
  • 一個是為什么會這樣,因為自定義實現(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);
      // 返回值會在調(diào)用方法時,被賦值,從上述4.1可看出,只有不傳值時才會把結(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é)果,就直接取出,多個或空,不處理
  private List<Object> collapseSingleResultList(List<Object> multipleResults) {
    return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
  }

總結(jié)

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

相關(guān)文章

  • Java實現(xiàn)圖片拼接

    Java實現(xiàn)圖片拼接

    這篇文章主要為大家詳細介紹了Java實現(xiàn)圖片拼接的相關(guān)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • 詳解Java中ArrayList類

    詳解Java中ArrayList類

    這篇文章主要介紹了Java中ArrayList類,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • SpringBoot詳細列舉常用注解的說明

    SpringBoot詳細列舉常用注解的說明

    在開發(fā)SpringBoot程序的過程中,有可能與其他業(yè)務(wù)系統(tǒng)進行對接開發(fā),獲取封裝公共的API接口等等,下面這篇文章主要給大家介紹了關(guān)于SpringBoot常見的注解的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • 基于TCP通信丟包原因總結(jié)(推薦)

    基于TCP通信丟包原因總結(jié)(推薦)

    下面小編就為大家?guī)硪黄赥CP通信丟包原因總結(jié)(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • Mybatis 中的一對一,一對多,多對多的配置原則示例代碼

    Mybatis 中的一對一,一對多,多對多的配置原則示例代碼

    這篇文章主要介紹了 Mybatis 中的一對一,一對多,多對多的配置原則示例代碼,需要的朋友可以參考下
    2017-03-03
  • Java的LinkedHashSet源碼深入講解

    Java的LinkedHashSet源碼深入講解

    這篇文章主要介紹了Java的LinkedHashSet源碼深入講解,LinkedHashSet是HashSet的子類,而由于HashSet實現(xiàn)了Set接口,因此LinkedHashSet也間接實現(xiàn)了Set類,LinkedHashSet類屬于java.base模塊,java.util包下,需要的朋友可以參考下
    2023-09-09
  • Java的this關(guān)鍵字的使用與方法的重載相關(guān)知識

    Java的this關(guān)鍵字的使用與方法的重載相關(guān)知識

    這篇文章主要介紹了Java的this關(guān)鍵字的使用與方法的重載相關(guān)知識,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • 第一次使用Android Studio時你應(yīng)該知道的一切配置(推薦)

    第一次使用Android Studio時你應(yīng)該知道的一切配置(推薦)

    這篇文章主要介紹了第一次使用Android Studio時你應(yīng)該知道的一切配置(推薦) ,需要的朋友可以參考下
    2017-09-09
  • springboot獲取根目錄下lib目錄下文件位置

    springboot獲取根目錄下lib目錄下文件位置

    在 Spring Boot 項目中,如果你有一個 lib 目錄,并且需要訪問這個目錄下的文件,你可以通過幾種不同的方式來獲取該文件的位置,下面小編就來和大家詳細講講
    2024-12-12
  • java實現(xiàn)List中對象排序的方法

    java實現(xiàn)List中對象排序的方法

    這篇文章主要介紹了java實現(xiàn)List中對象排序的方法,涉及Java中的遍歷與對象操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11

最新評論