在用mybatis作为持久层框架时,有时候会有需要进行批量增删改的操作。
百度了一下,大致有两种方法,一种是拼接SQL的方式。类似这样:
<insert id="insertList" parameterType="java.util.List" >
insert into t_project ( projectid,productid ) values
<foreach collection="list" item="item" index="index" separator="," >
( #{item.projectid,jdbcType=CHAR},#{item.productid,jdbcType=CHAR} )
</foreach>
</insert>
这种方法局限性太多,sql长度有限制,clob类型不能这样使用,貌似只能用于insert语句。所以完全不考虑。
第二种是用mybatis提供的BatchExecutor进行批处理操作。类似这样:
SqlSession session =sessionFactory.openSession(ExecutorType.BATCH,false);
for(int i=0;i<10;i++){
session.update("com.com.hcd.mybatis.interfaces.CommonMapper.update",getParam());
}
session.flushStatements();
session.commit();
这里的"com.com.hcd.mybatis.interfaces.CommonMapper.update"就是xml配置里namespace加<update>标签的id或mapper接口的class名+方法名。这种方式新开启了一个新事务,在mybatis和spring结合的情况下,这个事务没有被spring控制到。只能手动进行提交。
如果要让spring控制批处理事务,那只能所有的事务都使用批处理模式,即SqlSessionTemplate设置executorType为batch,但是这样会有问题,如:使用数据库自增主键则无法获得返回的主键,我这边的系统ID都是在java内存生成uuid后再插入到数据库的,所以这个没有影响;增删改操作无法返回影响行数,因为batchExecutor中update方法默认放回的是一个int常量。(其原因都是因为此时sql尚未在数据库执行,所以无法获得这些信息)。
所以,这个方法也不好。根本原因是mybatis在创建SqlSession的时候,去实例化executor时,就决定了执行sql的方式是不是batch方式。在实例化SImpleExector的时候,则表明该事务的所有增删改方法会立刻在数据库执行该语句,实例化BatchExector的时候,则表明该事务的所有增删改方法都是 以batch方式,不会立刻在数据库执行。即mybatis框架本身限制了你不能灵活的在一次事务中选择是否要以批处理的方式执行。
所以,我们就会想着去拓展mybatis框架,我这边mybatis版本是3.2.2。如果BatchExector在执行update方法后立刻执行doFlushStatements,则相当于SImpleExector下执行update方法。即batchExector工作机制已经满足我们的需求,想法是扩展新的执行器继承BatchExector,重写doUpdate方法,判断是否要以批处理方式执行,对应是否调用doFlushStatements。
下面是做了一个简单的测试,测试要点是想看用批处理做频繁的更新操作和非批处理做频繁的更新操作,效率差多少,BatchExector执行addBatch后立刻doFlushStatements,和SImpleExector执行update方法是否存在性能的差距。测试用例如下:
package com.hcd.mybatis.demo;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* Created by cd_huang on 2017/8/21.
*/
@Component
public class BatchMybatisTest {
@Autowired
private SqlSessionFactory sessionFactory;
public void test(){
int group =26;
long times[][] =new long[group][3];
for(int i=0;i<group;i++){
times[i][0] = batchExecutorBatchExecute(10+i*50);
times[i][1] =batchExecutorSimpleExecute(10+i*50);
times[i][2] =simpleExecutorSimpleExecute(10+i*50);
}
for(int i=1;i<group;i++){
System.out.println("Batch - Executor - Batch - Execute: " + times[i][0] + " ms");
System.out.println("Batch - Executor - Simple - Execute: " + times[i][1] + " ms");
System.out.println("Simple - Executor - Simple - Execute: " + times[i][2] + " ms");
}
}
public long batchExecutorBatchExecute(int group){
SqlSession session =sessionFactory.openSession(ExecutorType.BATCH,false);
long time = System.currentTimeMillis();
for(int i=0;i<group;i++){
session.update("com.com.hcd.mybatis.interfaces.CommonMapper.update",getParam());
}
session.flushStatements();
time = System.currentTimeMillis() - time;
session.commit();
return time;
}
public long batchExecutorSimpleExecute(int group){
SqlSession session =sessionFactory.openSession(ExecutorType.BATCH,false);
long time = System.currentTimeMillis();
for(int i=0;i<group;i++){
session.update("com.com.hcd.mybatis.interfaces.CommonMapper.update",getParam());
session.flushStatements();
}
time = System.currentTimeMillis() - time;
session.commit();
return time;
}
public long simpleExecutorSimpleExecute(int group){
SqlSession session =sessionFactory.openSession();
long time = System.currentTimeMillis();
for(int i=0;i<group;i++){
session.update("com.com.hcd.mybatis.interfaces.CommonMapper.update",getParam());
}
time = System.currentTimeMillis() - time;
session.commit();
return time;
}
private static Map<String,Object> getParam(){
Map param=new HashMap();
return param;
}
}
执行结果如下:
Batch - Executor - Batch - Execute: 123 ms
Batch - Executor - Simple - Execute: 271 ms
Simple - Executor - Simple - Execute: 197 ms
Batch - Executor - Batch - Execute: 181 ms
Batch - Executor - Simple - Execute: 524 ms
Simple - Executor - Simple - Execute: 596 ms
Batch - Executor - Batch - Execute: 238 ms
Batch - Executor - Simple - Execute: 664 ms
Simple - Executor - Simple - Execute: 523 ms
Batch - Executor - Batch - Execute: 370 ms
Batch - Executor - Simple - Execute: 747 ms
Simple - Executor - Simple - Execute: 814 ms
Batch - Executor - Batch - E