`
xiangshouxiyang
  • 浏览: 49225 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

扩展mybatis重写mybatis执行器实现灵活批处理

阅读更多

     在用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 - Execute: 311 ms
Batch - Executor - Simple - Execute: 688 ms
Simple - Executor - Simple - Execute: 661 ms
Batch - Executor - Batch - Execute: 357 ms
Batch - Executor - Simple - Execute: 799 ms
Simple - Executor - Simple - Execute: 765 ms
Batch - Executor - Batch - Execute: 431 ms
Batch - Executor - Simple - Execute: 917 ms
Simple - Executor - Simple - Execute: 878 ms
Batch - Executor - Batch - Execute: 471 ms
Batch - Executor - Simple - Execute: 991 ms
Simple - Executor - Simple - Execute: 848 ms
Batch - Executor - Batch - Execute: 339 ms
Batch - Executor - Simple - Execute: 1004 ms
Simple - Executor - Simple - Execute: 1087 ms
Batch - Executor - Batch - Execute: 820 ms
Batch - Executor - Simple - Execute: 1082 ms
Simple - Executor - Simple - Execute: 1443 ms
Batch - Executor - Batch - Execute: 746 ms
Batch - Executor - Simple - Execute: 1535 ms
Simple - Executor - Simple - Execute: 1514 ms
Batch - Executor - Batch - Execute: 875 ms
Batch - Executor - Simple - Execute: 1696 ms
Simple - Executor - Simple - Execute: 1677 ms
Batch - Executor - Batch - Execute: 810 ms
Batch - Executor - Simple - Execute: 1626 ms
Simple - Executor - Simple - Execute: 1576 ms
Batch - Executor - Batch - Execute: 748 ms
Batch - Executor - Simple - Execute: 1506 ms
Simple - Executor - Simple - Execute: 1375 ms
Batch - Executor - Batch - Execute: 751 ms
Batch - Executor - Simple - Execute: 1544 ms
Simple - Executor - Simple - Execute: 1387 ms
Batch - Executor - Batch - Execute: 689 ms
Batch - Executor - Simple - Execute: 1536 ms
Simple - Executor - Simple - Execute: 1490 ms
Batch - Executor - Batch - Execute: 772 ms
Batch - Executor - Simple - Execute: 1995 ms
Simple - Executor - Simple - Execute: 2242 ms
Batch - Executor - Batch - Execute: 1143 ms
Batch - Executor - Simple - Execute: 2530 ms
Simple - Executor - Simple - Execute: 2708 ms
Batch - Executor - Batch - Execute: 1705 ms
Batch - Executor - Simple - Execute: 2600 ms
Simple - Executor - Simple - Execute: 2745 ms
Batch - Executor - Batch - Execute: 1427 ms
Batch - Executor - Simple - Execute: 2516 ms
Simple - Executor - Simple - Execute: 2574 ms
Batch - Executor - Batch - Execute: 1191 ms
Batch - Executor - Simple - Execute: 2157 ms
Simple - Executor - Simple - Execute: 2142 ms
Batch - Executor - Batch - Execute: 708 ms
Batch - Executor - Simple - Execute: 2170 ms
Simple - Executor - Simple - Execute: 2173 ms
Batch - Executor - Batch - Execute: 818 ms
Batch - Executor - Simple - Execute: 2415 ms
Simple - Executor - Simple - Execute: 2419 ms
Batch - Executor - Batch - Execute: 1140 ms
Batch - Executor - Simple - Execute: 2570 ms
Simple - Executor - Simple - Execute: 2244 ms
Batch - Executor - Batch - Execute: 785 ms
Batch - Executor - Simple - Execute: 2205 ms
Simple - Executor - Simple - Execute: 2181 ms
     可以看出来,批处理模式下,进行大量update操作时,性能差不多可以提升60-70%。且BatchExector执行addBatch后立刻doFlushStatements,和SImpleExector执行update方法消耗的时候非常接近。

 

     测试表明了我们拓展mybatis的框架思路是有可行性的。下面我直接贴代码介绍下我如何拓展mybatis框架实现灵活批处理。

     扩展入口是利用mybatis提供的拦截器机制,实现自定义的代理执行器对象。需要在mybatis-config.xml里配置

<plugin interceptor="com.com.hcd.mybatis.exector.ProxyExecutorInterceptor" />
 

 

ProxyExecutorInterceptor:

package com.hcd.mybatis.exector;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Invocation;

import java.lang.reflect.Proxy;
import java.util.Properties;


/**
 * 使用插件生成代理执行器
 * Created by cd_huang on 2017/8/22.
 */
public class ProxyExecutorInterceptor implements Interceptor {

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		return null;
	}

	@Override
	public Object plugin(Object target) {
		if (target instanceof Executor == false) {
			return target;
		}
		return Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(),
				new ProxyExecutorHandler((Executor)target));
	}

	@Override
	public void setProperties(Properties properties) {
	}
}
 这里没用默认的Plugin.wrap()来代理对象,而是用我们自定义的ProxyExecutorHandler来进行代理控制。

 

ProxyExecutorHandler:

package com.hcd.mybatis.exector;

import com.hcd.common.SpringContextUtil;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.*;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.reflection.ReflectionException;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理拦截器,控制ComplexExecutor的初始化时机
 * Created by cd_huang on 2017/8/23.
 */
public class ProxyExecutorHandler implements InvocationHandler {

	private Executor target;

	private Executor realTarget;

	private boolean isInit = false;

	public ProxyExecutorHandler(Executor target) {
		this.target = target;
		MybatisExecutorContext.bindExecutor(this);
		if (MybatisExecutorContext.initComplexExecutorImmediately) {
			init();
		}

	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (!isInit && MybatisExecutorContext.isOpenExecutorMode()) {
			init();
		}
		try {
			return method.invoke(this.target, args);
		} finally {
			String methodName = method.getName();
			if ("commit".equals(methodName) || "rollback".equals(methodName) || "close".equals(methodName)) {
				MybatisExecutorContext.clean();
			}
		}
	}

	public void init() {
		this.target = initComplexExecutor(this.target);
		isInit = true;
	}

	/**
	 * 初始化ComplexExecutor
	 *
	 * @param target
	 * @return
	 */
	public Executor initComplexExecutor(Executor target) {
		Executor initialTarget = target;
		Object[] result = ProxyExecutorHandler.getLastPluginFromJdkProxy(target);
		Object parentPlugin = result[0];
		target = (Executor) result[1];
		if (target instanceof ComplexExecutor) {
			realTarget =target;
			return initialTarget;
		}
		if (target instanceof BaseExecutor) {
			ComplexExecutor newTarget = new ComplexExecutor(ProxyExecutorHandler.getConfiguration(), target.getTransaction(), getOriginalExecutorType((Executor)target));
			realTarget =newTarget;
			if (parentPlugin == null) {
				return newTarget;
			} else {
				//替换掉Plugin的target属性
				try {
					Field targetField = FieldUtils.getField(Plugin.class, "target", true);
					FieldUtils.writeField(targetField, parentPlugin, newTarget, true);
					return initialTarget;
				} catch (Throwable e) {
					throw new ReflectionException("replace property 'target' of Plugin error !", e);
				}
			}
		}
		//替换掉CachingExecutor的delegate属性
		if (target instanceof CachingExecutor) {
			try {
				Field delegateField = FieldUtils.getField(CachingExecutor.class, "delegate", true);
				Object delegate = FieldUtils.readField(delegateField, target, true);
				if (delegate instanceof BaseExecutor) {
					Executor newDelegate = new ComplexExecutor(ProxyExecutorHandler.getConfiguration(), ((BaseExecutor) delegate).getTransaction(), getOriginalExecutorType((Executor)delegate));
					realTarget =newDelegate;
					FieldUtils.writeField(delegateField, target, newDelegate, true);
					return initialTarget;
				}
			} catch (Throwable e) {
				throw new ReflectionException("replace property 'delegate' of CachingExecutor error !", e);
			}

		}
		throw new ExecutorException("init ComplexExecutor error !");
	}

	/**
	 * 获得jdk代理里最里层的一个Plugin对象
	 *
	 * @param proxy
	 * @return 返回 最里层的一个Plugin对象 和 Plugin的target属性
	 */
	public static Object[] getLastPluginFromJdkProxy(Object proxy) {
		if (proxy instanceof Proxy) {
			InvocationHandler h = Proxy.getInvocationHandler(proxy);
			Object target;
			try {
				Field targetField = FieldUtils.getField(Plugin.class, "target", true);
				target = FieldUtils.readField(targetField, h, true);
			} catch (Throwable e) {
				throw new ReflectionException("get property 'target' of Plugin error !", e);
			}
			if (target instanceof Proxy) {
				return ProxyExecutorHandler.getLastPluginFromJdkProxy(target);
			} else {
				return new Object[]{h, target};
			}
		} else {
			return new Object[]{null, proxy};
		}
	}

	private static Configuration configuration;

	public static Configuration getConfiguration() {
		if (configuration == null) {
			//从spring上下文中拿到SqlSessionFactory,从而拿到configuration
			configuration = SpringContextUtil.getBean(SqlSessionFactory.class).getConfiguration();
		}
		return configuration;
	}

	public ExecutorType getOriginalExecutorType() {
		if(realTarget!=null){
			return getOriginalExecutorType(realTarget);
		}
		throw new ExecutorException("ComplexExecutor not init !");
	}

	public static ExecutorType getOriginalExecutorType(Executor executor) {
		if(executor instanceof ComplexExecutor){
			return ((ComplexExecutor)executor).getOriginalExecutorType();
		}
		if (BatchExecutor.class.getName().equals(executor.getClass().getName())) {
			return ExecutorType.BATCH;
		} else {
			return ExecutorType.SIMPLE;
		}
	}

	public Executor getRealTarget(){
		return realTarget;
	}

	public Executor getTarget() {
		return target;
	}

	public void setTarget(Executor target) {
		this.target = target;
	}
}

 主要是控制把SimpleExector或BatchExector替换成我们自定义的执行器对象ComplexExecutor。

 

ComplexExecutor:

package com.hcd.mybatis.exector;

import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.transaction.Transaction;

import java.sql.SQLException;
import java.util.List;

/**
 * 扩展mybatis支持批处理和非批处理两种模式的复合执行器
 * Created by cd_huang on 2017/8/22.
 */
public class ComplexExecutor extends BatchExecutor{

	private ExecutorType originalExecutorType;

	public ComplexExecutor(Configuration configuration, Transaction transaction,ExecutorType originalExecutorType) {
		super(configuration, transaction);
		this.originalExecutorType =originalExecutorType;

	}

	public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
		int result =super.doUpdate(ms,parameterObject);
		if(!MybatisExecutorContext.isBatchExecutorMode()){
			List<BatchResult> results=this.doFlushStatements(false);
			return results.get(0).getUpdateCounts()[0];
		}
		return result;
	}

	public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
		List<BatchResult> results =super.doFlushStatements(isRollback);
		MybatisExecutorContext.getCheckBatchResultHook().checkBatchResult(results);
		return results;
	}

	public ExecutorType getOriginalExecutorType() {
		return originalExecutorType;
	}

}
      该执行器重写了doUpdate()方法,判断是否要使用批处理模式,从而判断是否执行doFlushStatements。       判断是否要执行批处理模式,是在MybatisExecutorContext类中,利用线程变量,来主动设置的。MybatisExecutorContext:
package com.hcd.mybatis.exector;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.session.ExecutorType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

/**
 * mybatis执行器上下文
 * Created by cd_huang on 2017/8/22.
 */
public class MybatisExecutorContext {

	private static Logger logger = LoggerFactory.getLogger(MybatisExecutorContext.class);

	private static final ThreadLocal<ExecutorType> currentExecutorType = new ThreadLocal<>();

	private static final ThreadLocal<ProxyExecutorHandler> executorResource = new ThreadLocal<>();

	private static final ThreadLocal<CheckBatchResultHook> checkBatchResultHook = new ThreadLocal<>();
	/**
	 * 是否立刻初始化ComplexExecutor
	 */
	public static boolean initComplexExecutorImmediately =true;

	public static void openSimpleExecutorMode(){
		currentExecutorType.set(ExecutorType.SIMPLE);
	}

    public static void openBatchExecutorMode(){
	    currentExecutorType.set(ExecutorType.BATCH);
    }

	public static void closeExecutorMode(){
		currentExecutorType.remove();
	}

	public static List<BatchResult> doFlushStatements(){
		ProxyExecutorHandler interceptor =executorResource.get();
		if(interceptor==null){
			return Collections.emptyList();
		}
		Executor executor =interceptor.getRealTarget();
		if(executor instanceof ComplexExecutor){
			try {
				return ((ComplexExecutor)executor).doFlushStatements(false);
			} catch (SQLException e) {
				throw new RuntimeException("doFlushStatements error!",e);
			}
		}
		throw new ExecutorException("doFlushStatements must invoke on ExecutorType.BATCH!");
	}

	public static void bindExecutor(ProxyExecutorHandler interceptor){
		executorResource.set(interceptor);
	}

	public static boolean isOpenExecutorMode(){
		return currentExecutorType.get()!=null;
	}

	public static Boolean isBatchExecutorMode() {
		ExecutorType executorType =currentExecutorType.get();
		if(executorType==null){
			ProxyExecutorHandler interceptor =executorResource.get();
			executorType =interceptor==null?null:interceptor.getOriginalExecutorType();
		}
		return ExecutorType.BATCH.equals(executorType);
	}

    public static void clean(){
	    currentExecutorType.remove();
	    executorResource.remove();
    }

    private static CheckBatchResultHook defaultCheckBatchResultHook =new DefaultCheckBatchResultHook();

	public static CheckBatchResultHook getCheckBatchResultHook(){
		CheckBatchResultHook hook =checkBatchResultHook.get();
		return hook==null?defaultCheckBatchResultHook:hook;
	}

	public static void setCheckBatchResultHook(CheckBatchResultHook hook){
		checkBatchResultHook.set(hook);
	}

	/**
	 * 校验批处理结果的默认机制(默认更新影响记录数为0的时候logger打印警告信息)
	 */
	private static class DefaultCheckBatchResultHook implements CheckBatchResultHook{
	    @Override
	    public boolean checkBatchResult(List<BatchResult> results) {
		    for(BatchResult result:results){
			    for(int i=0;i<result.getUpdateCounts().length;i++){
                    if(result.getUpdateCounts()[i]<=0){
	                    logger.warn(" sql statementId "+result.getMappedStatement().getId()+","+result.getMappedStatement().getSqlCommandType().name()+" effect 0 rows ! ");
                    }
			    }
		    }
		    return true;
	    }
    }
}
 该执行器还重写了doFlushStatements方法,调用了MybatisExecutorContext.getCheckBatchResultHook().checkBatchResult(results);

 

主要是用于可以自定义校验方法去校验批处理模式下的影响行数是否符合预计。

CheckBatchResultHook:

package com.hcd.mybatis.exector;

import org.apache.ibatis.executor.BatchResult;

import java.util.List;

/**
 * 校验批处理结果
 * Created by cd_huang on 2017/8/24.
 */
public interface CheckBatchResultHook {

	boolean checkBatchResult(List<BatchResult> results);
}

 默认的校验实现在MybatisExecutorContext的内部类DefaultCheckBatchResultHook中,默认增删改的影响行数小于等于0时,打印警告日志。这里可以看是否需要改成直接抛异常。

       然后用新的执行器在complexExecutor,spring事务里执行,测试用例BatchMybatisTest2:

package com.hcd.mybatis.demo;

import com.hcd.common.SpringContextUtil;
import com.hcd.mybatis.exector.MybatisExecutorContext;
import com.hcd.mybatis.interfaces.CommonMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by cd_huang on 2017/8/28.
 */
@Component
public class BatchMybatisTest2 {
	@Autowired
	private CommonMapper commonMapper;

	private TransactionTemplate transactionTemplate;

    private TransactionTemplate getTransactionTemplate(){
	    if(transactionTemplate==null){
		    transactionTemplate =new TransactionTemplate();
		    transactionTemplate.setTransactionManager(SpringContextUtil.getBean(PlatformTransactionManager.class));
		    transactionTemplate.setPropagationBehavior(3);
	    }
	    return transactionTemplate;
    }
	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(final int group){
		return getTransactionTemplate().execute( new TransactionCallback<Long>(){

			@Override
			public Long doInTransaction(TransactionStatus status) {
				MybatisExecutorContext.openBatchExecutorMode();
				long time = System.currentTimeMillis();
				for(int i=0;i<group;i++){
					commonMapper.update();
				}
				MybatisExecutorContext.doFlushStatements();
				time = System.currentTimeMillis() - time;
				return time;
			}
		});


	}
	public long batchExecutorSimpleExecute(final int group){
		return getTransactionTemplate().execute( new TransactionCallback<Long>(){
			@Override
			public Long doInTransaction(TransactionStatus status) {
				long time = System.currentTimeMillis();
				for(int i=0;i<group;i++){
					commonMapper.update();
				}
				time = System.currentTimeMillis() - time;
				return time;
			}
		});
	}

	public long simpleExecutorSimpleExecute(final int group){
		MybatisExecutorContext.initComplexExecutorImmediately =false;
		return getTransactionTemplate().execute( new TransactionCallback<Long>(){
			@Override
			public Long doInTransaction(TransactionStatus status) {
				long time = System.currentTimeMillis();
				for(int i=0;i<group;i++){
					commonMapper.update();
				}
				time = System.currentTimeMillis() - time;
				return time;
			}
		});
	}
}


 测试结果如下:

Batch - Executor - Batch - Execute: 62 ms
Batch - Executor - Simple - Execute: 121 ms
Simple - Executor - Simple - Execute: 148 ms
Batch - Executor - Batch - Execute: 79 ms
Batch - Executor - Simple - Execute: 254 ms
Simple - Executor - Simple - Execute: 219 ms
Batch - Executor - Batch - Execute: 78 ms
Batch - Executor - Simple - Execute: 327 ms
Simple - Executor - Simple - Execute: 281 ms
Batch - Executor - Batch - Execute: 113 ms
Batch - Executor - Simple - Execute: 387 ms
Simple - Executor - Simple - Execute: 388 ms
Batch - Executor - Batch - Execute: 101 ms
Batch - Executor - Simple - Execute: 504 ms
Simple - Executor - Simple - Execute: 495 ms
Batch - Executor - Batch - Execute: 137 ms
Batch - Executor - Simple - Execute: 526 ms
Simple - Executor - Simple - Execute: 559 ms
Batch - Executor - Batch - Execute: 154 ms
Batch - Executor - Simple - Execute: 606 ms
Simple - Executor - Simple - Execute: 631 ms
Batch - Executor - Batch - Execute: 198 ms
Batch - Executor - Simple - Execute: 699 ms
Simple - Executor - Simple - Execute: 793 ms
Batch - Executor - Batch - Execute: 227 ms
Batch - Executor - Simple - Execute: 858 ms
Simple - Executor - Simple - Execute: 851 ms
Batch - Executor - Batch - Execute: 185 ms
Batch - Executor - Simple - Execute: 812 ms
Simple - Executor - Simple - Execute: 942 ms
Batch - Executor - Batch - Execute: 227 ms
Batch - Executor - Simple - Execute: 1057 ms
Simple - Executor - Simple - Execute: 1528 ms
Batch - Executor - Batch - Execute: 243 ms
Batch - Executor - Simple - Execute: 1037 ms
Simple - Executor - Simple - Execute: 993 ms
Batch - Executor - Batch - Execute: 158 ms
Batch - Executor - Simple - Execute: 1091 ms
Simple - Executor - Simple - Execute: 1083 ms
Batch - Executor - Batch - Execute: 264 ms
Batch - Executor - Simple - Execute: 1391 ms
Simple - Executor - Simple - Execute: 1133 ms
Batch - Executor - Batch - Execute: 160 ms
Batch - Executor - Simple - Execute: 1232 ms
Simple - Executor - Simple - Execute: 1247 ms
Batch - Executor - Batch - Execute: 224 ms
Batch - Executor - Simple - Execute: 1287 ms
Simple - Executor - Simple - Execute: 1344 ms
Batch - Executor - Batch - Execute: 207 ms
Batch - Executor - Simple - Execute: 1379 ms
Simple - Executor - Simple - Execute: 1367 ms
Batch - Executor - Batch - Execute: 280 ms
Batch - Executor - Simple - Execute: 1473 ms
Simple - Executor - Simple - Execute: 1469 ms
Batch - Executor - Batch - Execute: 344 ms
Batch - Executor - Simple - Execute: 1544 ms
Simple - Executor - Simple - Execute: 1582 ms
Batch - Executor - Batch - Execute: 511 ms
Batch - Executor - Simple - Execute: 1747 ms
Simple - Executor - Simple - Execute: 1612 ms
Batch - Executor - Batch - Execute: 263 ms
Batch - Executor - Simple - Execute: 1772 ms
Simple - Executor - Simple - Execute: 1812 ms
Batch - Executor - Batch - Execute: 343 ms
Batch - Executor - Simple - Execute: 1799 ms
Simple - Executor - Simple - Execute: 1906 ms
Batch - Executor - Batch - Execute: 340 ms
Batch - Executor - Simple - Execute: 1855 ms
Simple - Executor - Simple - Execute: 1833 ms
Batch - Executor - Batch - Execute: 354 ms
Batch - Executor - Simple - Execute: 1925 ms
Simple - Executor - Simple - Execute: 1932 ms
Batch - Executor - Batch - Execute: 381 ms
Batch - Executor - Simple - Execute: 2014 ms
Simple - Executor - Simple - Execute: 2184 ms

 如预期所料,实现了灵活的批处理效果。

分享到:
评论

相关推荐

    mybatis拦截器实现通用权限字段添加的方法

    本文将详细介绍如何使用MyBatis拦截器来实现通用权限字段添加,达到灵活、可靠、可维护的数据库操作。 MyBatis拦截器是什么 MyBatis拦截器是一个接口,用于拦截MyBatis的Executor对象,以便在执行SQL语句之前或...

    mybatis使用拦截器实现分页操作

    除了上述基本的实现方式,还可以考虑结合MyBatis的`ExecutorType.SIMPLE`或`ExecutorType.BATCH`执行器,以及`boundSql`对象来优化分页性能。例如,可以避免在`SIMPLE`模式下对每个分页请求都进行数据库连接的创建和...

    Mybatis学习- 拦截器-实现分页

    在Mybatis框架中,拦截器(Interceptor)是一个强大的工具,它可以让我们在Mybatis执行SQL之前或之后做一些额外的操作,比如日志记录、权限检查、事务管理等。在本主题中,我们将深入探讨如何利用Mybatis的拦截器...

    通过Mybatis拦截器自动定位慢SQL并记录日志

    Mybatis拦截器(Interceptor)是一种插件机制,它允许我们在Mybatis执行SQL语句之前或之后进行自定义操作,比如统计SQL执行时间、添加日志等。拦截器基于Java的动态代理实现,可以拦截Mapper接口方法的调用。 接...

    spring boot mybatis 国际化 拦截器

    在本文中,我们将深入探讨如何在Spring Boot应用中整合MyBatis,实现MySQL数据库连接,以及如何利用Spring MVC和拦截器来实现国际化(i18n)功能。此外,我们还将提及IIS 12作为可能的Web服务器选项。 首先,Spring...

    由浅入深分析mybatis通过动态代理实现拦截器(插件)的原理

    在 MyBatis 中,拦截器需要实现 `org.apache.ibatis.plugin.Interceptor` 接口,并重写 `intercept` 方法。这个方法会在目标方法执行前后被调用,参数 `Invocation` 包含了目标方法的信息,包括方法对象、方法参数等...

    创建springboot + mybatis项目,实现登录、注册功能详细过程

    1. 创建自定义拦截器类,实现`HandlerInterceptor`接口,重写`preHandle`、`postHandle`和`afterCompletion`方法。 2. 在`WebMvcConfigurer`的实现类中,通过`addInterceptors`方法添加拦截器,指定拦截的URL和排除...

    Mybatis+注解轻松实现脱敏

    1. 创建一个自定义的MyBatis插件,实现`Interceptor`接口,并重写`intercept`方法。在这个方法中,根据注解判断是否需要对参数或结果集进行处理。 2. 在`Configuration`类中注册这个插件,确保在MyBatis执行SQL时能...

    mybatis分页拦截器(自动封装版)剖析.pdf

    在自动封装版的MyBatis分页拦截器中,开发者通常会创建一个拦截器类,该类会拦截到执行SQL的时机,然后在SQL语句中动态添加分页相关的条件,如LIMIT和OFFSET子句,以实现数据的分页展示。 分页拦截器的核心思想是...

    Mybatis分库分表扩展插件

    Mybatis作为一款强大的Java持久层框架,其灵活性使得扩展分库分表功能变得可能。本文将深入探讨Mybatis分库分表扩展插件的原理与实践。 一、Mybatis简介与分库分表需求 Mybatis是一款优秀的持久层框架,它支持定制...

    Mybatis中拦截器的简单实现方法

    定义好的拦截器会按照顺序被Mybatis执行。 由于Mybatis的架构设计,拦截器的实现和使用相对灵活。可以通过实现Interceptor接口并重写其方法来自定义拦截逻辑,还可以通过@Intercepts和@Signature注解来指定拦截器...

    free mybatis plugins

    通过实现Interceptor接口并重写intercept方法,开发者可以实现自定义的拦截逻辑。 4. **Free MyBatis Plugins的功能**:这款免费插件可能包含以下功能: - SQL智能提示:在编写Mapper XML文件或者Mapper接口时,...

    springBoot+mybatis简单demo完整详细版

    Spring Boot以其快速启动、自动化配置和开箱即用的特点,大大简化了Java应用程序的开发过程,而MyBatis作为轻量级的持久层框架,提供了灵活的SQL映射机制,使得数据库操作更为便捷。在这个教程中,我们将不仅了解这...

    javafx 整合 sparingboot mybatis

    在 JavaFX 应用中整合 SpringBoot 和 MyBatis 可以让开发者利用 Spring 的自动化配置和依赖注入,以及 MyBatis 的灵活数据库操作,构建出高效且易于维护的桌面应用。 整合 JavaFX、SpringBoot 和 MyBatis 的主要...

    mybatis-pagination-master

    mybatis-pagination是一款针对MyBatis的分页插件,它通过拦截器的方式,实现了对原生MyBatis的动态SQL进行拦截,自动添加分页相关的SQL语句,从而实现数据库的分页查询。此插件支持多种主流数据库,如MySQL、Oracle...

    MyBatis拦截器实现分页功能的实现方法

    分页拦截器需要实现MyBatis提供的Interceptor接口,并重写intercept方法,以便在SQL执行前进行拦截。在intercept方法中,需要根据Page对象计算出的参数(dbIndex和dbNumber)来修改原始的SQL语句,添加分页逻辑。...

    02-02-03-MyBatis插件原理及Spring集成1

    总的来说,理解 MyBatis 插件原理和自定义插件的编写,可以帮助我们更好地扩展 MyBatis 的功能,实现更灵活的数据库操作。同时,掌握 Spring 集成 MyBatis 的方法,可以确保事务管理和资源管理的正确性,提升系统的...

    springmybatis

    以前曾经用过ibatis,这是mybatis的前身,当时在做项目时,感觉很不错,比hibernate灵活。性能也比hibernate好。而且也比较轻量级,因为当时在项目中,没来的及做很很多笔记。后来项目结束了,我也没写总结文档。已经...

    mybatis-plugins

    这些拦截器可以注册到MyBatis的配置文件中,当MyBatis执行SQL时,会按照配置的顺序逐个调用拦截器的intercept()方法。 在下载并解压“mybatis-plugins”压缩包后,你会得到一个名为“mybatis-plugins”的文件,这个...

    扩展MyBatisPlus代码生成器实现自定义源码生成,可生成前端页面、vo对象、dto对象等代码

    通过深入研究源码、理解代码生成的流程,我们可以灵活地定制代码生成器,实现前端页面、VO对象、DTO对象等自定义代码的生成,从而提高开发效率,降低维护成本。在实践中,不断尝试和调试,结合官方文档和社区资源,...

Global site tag (gtag.js) - Google Analytics