批量处理的方式有很多种。这里我就不分析每种方法的优劣了,只是简单概括一下ExecutorType.BATCH的用法,学习不好。如果有不对的地方还请各位大侠指正。问题的起因是公司在写项目的时候,有自动对账的需求。它需要从文件中读取数万条数据,并插入到数据库中。随着业务的增长,可能会涨到几十万,所以插入需要进行批量操作,来看看我是怎么一步步踩坑的。简单看一下批处理背后的秘密,BatchExecutor批处理是JDBC编程中的另一种优化方式。JDBC在执行SQL语句时,会通过网络请求向数据库发送SQL语句和实际参数。一次执行一条SQL语句,一方面会减少请求包的负载,另一方面会增加网络通信的开销。时间。通过批处理,我们可以在JDBC客户端缓存多条SQL语句,然后在flush或者cache满了的时候,将多条SQL语句打包发送到数据库中执行,可以有效减少以上两方面的损失,从而提高系统性能。但是有一点需要特别注意:每次向数据库发送的SQL语句数量是有上限的。批量执行时如果超过上限,数据库会抛出异常,拒绝执行这批SQL语句。所以我们需要控制批量发送SQL语句的数量和频率。Version1-废话不多说,批处理的代码早前在项目的代码中就有了,伪代码如下:@ResourceprivateaMapperclassmapperinstanceobject;privateintBATCH=1000;privatevoiddoUpdateBatch(DateaccountDate,List数据){SqlSessionbatchSqlSession=null;try{if(data==null||data.size()==0){return;}batchSqlSession=sqlSessionFactory.openSession(ExecutorType.BATCH,false);for(intindex=0;indexintbatchUpdateOrInsert(Listdata,ToIntFunctionfunction){intcount=0;SqlSessionbatchSqlSession=sqlSessionFactory.openSession(ExecutorType.BATCH);try{for(intindex=0;indexmapper实例object.insert方法(item));这时候,我高兴地收工了,直到一两天后,导师问我,你考虑过这个业务的业绩吗?,后续量大,每天可能有10万多条数据。问我每天用多长时间,结果发现insert0.020,000到30,000条数据需要7分钟(不完全是因为这个问题,也是Oracleinsertstatements的原因,下面会介绍),,,哈哈,我笑不出来了,简直就是个bugmaker,我开始思考为什么这么慢,肯定是批处理没有生效,所以我就想为什么没有生效?版本3-标准写法我们知道上面提到了BatchExecutor执行器,我们知道每个SqlSession都会有一个Executor对象,这个对象就是执行SQL语句的幕后黑手,我们也知道当Spring与Mybatis使用的SqlSession是SqlSessionTemplate,默认是ExecutorType.SIMPLE。这时候你通过自动注入得到的Mapper对象其实就是publicExecutornewExecutor(Transactiontransaction,ExecutorTypeexecutorType){executorType=executorType==null?defaultExecutorType:executorType;executorType=executorType==null?ExecutorType.SIMPLE:executorType;Executorexecutor;if(ExecutorType.BATCH==executorType){executor=newBatchExecutor(this,transaction);}elseif(ExecutorType.REUSE==executorType){executor=newReuseExecutor(this,transaction);}else{executor=newSimpleExecutor(this,transaction);}if(cacheEnabled){executor=newCachingExecutor(executor);}executor=(Executor)interceptorChain.pluginAll(executor);returnexecutor;}那我们其实就是你需要通过sqlSessionFactory.openSession(ExecutorType.BATCH)获取一个sqlSession对象(此时里面的Executor为BatchExecutor)获取一个新的Mapper对象才能生效!!!那么我们把这个通用方法改一下,把MapperClass传入publicclassMybatisBatchUtils{/***一次处理1000条*/privatestaticfinalintBATCH_SIZE=1000;@ResourceprivateSqlSessionFactorysqlSessionFactory;/***批量处理修改或插入**@paramdata待处理的数据*@parammapperClassMybatisMapper类*@paramfunction自定义处理逻辑*@returnint受影响的总行数*/publicintbatchUpdateOrInsert(Listdata,ClassmapperClass,BiFunction功能){inti=1;SqlSessionbatchSqlSession=sqlSessionFactory.openSession(ExecutorType.BATCH);try{Umapper=batchSqlSession.getMapper(mapperClass);intsize=data.size();for(Telement:data){function.apply(element,mapper);if((i%BATCH_SIZE==0)||i==size){batchSqlSession.flushStatements();}i++;}//在非事务环境下强制提交,相当于无效的batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());}catch(Exceptione){batchSqlSession.rollback();thrownewCustomException(e);}finally{batchSqlSession.close();}returni-1;}}这会判断是否是事务环境,not如果是事务环境,本次commit设置的force值无效。这个在官网之前的截图中有提到用例:batchUtils.batchUpdateOrInsert(datacollection,xxxxx.class,(item,mapperinstanceobject)->mapperinstanceobject.insertmethod(item));附:Oracle批量插入优化我们都知道Oracle的主键序列生成策略和MySQL不一样,我们需要得到一个序列生成器,这里不再详细介绍,然后在MybatisGenerator生成的模板代码中,insert的id是这样获取的selectXXX.nextvalfromdual这样就相当于插入了10000条数据。事实上,插入和查询序列之间的总交互预计为20,000次,并且耗时超过10s。我们改为使用原来的Batch来插入。在这种情况下,只需要500多毫秒,也就是0.5秒。insertintotable_name(id,username,password)values(SEQ_USER.#{username},#{password})最后一个操作,批处理+语句优化,这个业务直接从7分钟多变成了10多秒,完美解决,庆祝一下~