jdbc mybatis中fetchsize使用及批处理过程

 更新时间:2026年07月07日 09:22:11   作者:qq_37556726  
想解决数据库批量操作卡死、内存溢出问题?本文详解JDBC与MyBatis分批拉取数据、批量插入与统一导出方案,手把手教你用ResultHandler与ExecutorType.BATCH高效处理海量数据,告别卡顿

简介

在操作数据库或者使用框架的时候避免不了批处理的使用场景,本文就这些场景讲解对应的方案

jdbc获取大量数据

场景,多用于导出一次性拉取大量数据,出现卡死,内存溢出问题,可以进行分批拉取数据然后最后统一导出或者分批导出

public class testMain {
    public static long importData(String sql){
        String url = "jdbc:mysql://127.0.0.1:3306/gsong?characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8";
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
        long allStart = System.currentTimeMillis();
        long count =0;

        Connection con = null;
        PreparedStatement ps = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            con = DriverManager.getConnection(url,"root","123456");
            ps = (PreparedStatement) con.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY,
                    ResultSet.CONCUR_READ_ONLY);
            ps.setFetchSize(Integer.MIN_VALUE);
            ps.setFetchDirection(ResultSet.FETCH_REVERSE);
            rs = ps.executeQuery();
            while (rs.next()) {

//此处处理业务逻辑
                count++;
                if(count%600000==0){
                    long end = System.currentTimeMillis();
                    System.out.println(" 写入到第  "+(count/600000)+" 个文件中!+++"+(end-allStart));
                    allStart=end;

                }

            }
            System.out.println("取回数据量为  "+count+" 行!");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if(rs!=null){
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(ps!=null){
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(con!=null){
                    con.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return count;

    }

    public static void main(String[] args) throws InterruptedException {

        String sql = "select * from user limit 0,4800000 ";
        importData(sql);

    }
}

mybatis获取大量数据

mapper文件

<select id="selectGxids" resultType="java.lang.String" fetchSize="1000">
   SELECT gxid from t_gxid
 </select>

自定义ResultHandler来分批处理结果集

public class GxidResultHandler implements ResultHandler<String> {
  // 这是每批处理的大小
  private final static int BATCH_SIZE = 1000;
  private int size;
  // 存储每批数据的临时容器
  private Set<String> gxids;

  public void handleResult(ResultContext<? extends String> resultContext) {
    // 这里获取流式查询每次返回的单条结果
    String gxid = resultContext.getResultObject();
    // 你可以看自己的项目需要分批进行处理或者单个处理,这里以分批处理为例
    gxids.add(gxid);
    size++;
    if (size == BATCH_SIZE) {
      handle();
    }
  }

  private void handle() {
    try {
      // 在这里可以对你获取到的批量结果数据进行需要的业务处理
    } finally {
      // 处理完每批数据后后将临时清空
      size = 0;
      gxids.clear();
    }
  }

  // 这个方法给外面调用,用来完成最后一批数据处理
  public void end(){
    handle();// 处理最后一批不到BATCH_SIZE的数据
  }
}

使用

@Service
public class ServiceImpl implements Service {
  @Autowired
  SqlSessionTemplate sqlSessionTemplate;

  public void method(){
    GxidResultHandler gxidResultHandler = new GxidResultHandler();
    sqlSessionTemplate.select("flowselect.Mapper.selectGxids", gxidResultHandler);
    gxidResultHandler.end();
  }
}
总结

 流式查询:内存会保持稳定,不会随着记录的增长而增长。其内存大小取决于批处理
 大 小 BATCH_SIZE的设置,该尺寸越大,内存会越大。所以BATCH_SIZE应该根据业务
 情况设置合适的大小。另外要切记每次处理完一批结果要记得释放存储每批数据的
 临时容器,即上文中的gxids.clear();

批处理方案

for循环一条条插入,或者 去实现的批量操作

<insert id="insertBatch">
		insert into user(name,age,is_delete) 
		<foreach collection="users" item="user" open="VALUES" close=";" separator=",">
			(#{user.name},#{user.age},#{user.isDelete})
		</foreach>
</insert>

List<User> users=new ArrayList<>();
		for(int i=0;i<10000;i++){
			users.add(new User(null,"赵敏"+i,i,"镇雄"+i,false));
		}
		long begin=System.currentTimeMillis();
		int insertBatch = mapper.insertBatch(users);
		long end=System.currentTimeMillis();
		System.out.println(end-begin);
		System.out.println(insertBatch);
		sqlSession.close();
		总结:
		这种方式数据量大时将会卡死
		改进后!!!直接for循环,50次提交
		public void batchTest() {
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
    List<Country> countries = mapper.selectAll();
    for (int i = 0; i < countries.size(); i++) {
        Country country = countries.get(i);
        country.setCountryname(country.getCountryname() + "Test");
        mapper.updateByPrimaryKey(country);
        //每 50 条提交一次
        if((i + 1) % 50 == 0){
            sqlSession.flushStatements();
        }
    }
    sqlSession.flushStatements();
}
总结:
每50次提交一次,是一种可行的方式
		

使用ExecutorType.BATCH创建SqlSession

public void test09(){
		SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
		IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
		
		long begin=System.currentTimeMillis();
		for(int i=0;i<10000;i++){
			mapper.insert(new User(null,"赵敏"+i,i,"镇雄"+i,false));
		}
		long end=System.currentTimeMillis();
		System.out.println(end-begin);
		sqlSession.close();
	}
	总结:不会被卡死,但实际运用中,选择改进后的自定义多少次提交更为友好

总结

批量提交只能应用于 insert, update, delete。

并且在批量提交使用时,如果在操作同一SQL时中间插入了其他数据库操作,就失去了其意义,所以在使用批量提交时,要控制好 SQL 执行顺序。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Maven统一版本管理的实现

    Maven统一版本管理的实现

    在使用Maven多模块结构工程时,配置版本是一个比较头疼的事,本文主要介绍了Maven统一版本管理的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • JAVA中对List进行查询

    JAVA中对List进行查询

    下面小编就为大家带来一篇在java List中进行模糊查询的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-08-08
  • spring boot使用sharding jdbc的配置方式

    spring boot使用sharding jdbc的配置方式

    这篇文章主要介绍了spring boot使用sharding jdbc的配置方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • Java线程创建的四种方式总结

    Java线程创建的四种方式总结

    这篇文章主要介绍了Java线程创建的四种方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • Java硬币翻转倍数递增试算实例

    Java硬币翻转倍数递增试算实例

    这篇文章主要介绍了Java硬币翻转倍数递增试算实例,有需要的朋友可以参考一下
    2013-12-12
  • Spring中的事务传播行为示例详解

    Spring中的事务传播行为示例详解

    这篇文章主要给大家介绍了关于Spring中事务传播行为的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • Spring Boot 完整启动流程详解

    Spring Boot 完整启动流程详解

    本文完整解析Spring Boot启动流程的9个核心阶段,结合源码与实战场景,帮助开发者深入理解框架运行机制,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • java求三个数的最大值的示例分享

    java求三个数的最大值的示例分享

    这篇文章主要介绍了java求三个数的最大值的示例分享,需要的朋友可以参考下
    2014-03-03
  • @ControllerAdvice 用法解析

    @ControllerAdvice 用法解析

    @ControllerAdvice就是@Controller 的增强版,@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用,这篇文章主要介绍了@ControllerAdvice 用法,需要的朋友可以参考下
    2022-11-11
  • SpringBoot项目如何打包成war包

    SpringBoot项目如何打包成war包

    使用SpringBoot 开发项目,由于内置了Tomcat,所以项目可以直接启动,部署到服务器的时候,直接打成 jar 包,就可以运行了,这篇文章主要介绍了SpringBoot项目如何打包成war包,需要的朋友可以参考下
    2024-07-07

最新评论