分库,通过mybatis得拦截器

1.思路:(1)多个数据库--这里有两个数据库,分配两个数据库连接池。

(2)AbstractRoutingDataSource定义了数据源,数据源是动态的。

public class DynamicDataSource extends AbstractRoutingDataSource{
	@Override
	protected Object determineCurrentLookupKey() {
		return DynamicDataSourceHolder.getDbType();
	}
}

(3)通过threadlocal来绑定当前数据源,防止多线程干扰。

public class DynamicDataSourceHolder {
	
	private static Logger log = LoggerFactory.getLogger(DynamicDataSourceHolder.class);
	
	private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
	
	public static final String DB_MASTER = "master";
	
	public static final String DB_SLAVE ="slave";
	
	public static String getDbType() {
		String db = contextHolder.get();
		if(db == null) {
			db = DB_MASTER;
		}
		return db;
	}
	public static void setDbType(String str) {
		log.debug("所使用的数据源为:"+str);
		if(str.equals(DB_MASTER)) {
			contextHolder.set(DB_MASTER);
		}else {
			contextHolder.set(DB_SLAVE);
		}
		
	}
	
	public static void clearDbType() {
		contextHolder.remove();
	}
}

(3)实现mybatis拦截器。拦截sql,通过判断是否有事务,增删改查等操作动态分库。

import java.util.Locale;
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class }) })
public class DynamicDataSourceInterceptor implements Interceptor {
	
	private static Logger log = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);
	
	private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// mybatis转换的sql参数
		Object[] objects = invocation.getArgs();
		// 记载了本次sql是增删改查哪一种信息
		MappedStatement ms = (MappedStatement) objects[0];
		String lookupKey = DynamicDataSourceHolder.DB_MASTER;
		// 判断是否是事务的
		boolean synchronizationActive = TransactionSynchronizationManager.isActualTransactionActive();
		if (synchronizationActive) {
			// 若是读方法
			if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
				// selectKey 为自增id查询主键(select last_insert_id())方法,使用主库
				if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
					lookupKey = DynamicDataSourceHolder.DB_MASTER;
				} else {
					BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);
					// 制表符,空格符替换成" "
					String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", "");
					if (sql.matches(REGEX)) {
						lookupKey = DynamicDataSourceHolder.DB_MASTER;
					} else {
						lookupKey = DynamicDataSourceHolder.DB_SLAVE;
					}
				}
			}
		} else {
			lookupKey = DynamicDataSourceHolder.DB_SLAVE;
		}
		log.debug("设置方法[{}] use [{}] Strategy, SqlCommanType[{}]", ms.getId(), lookupKey,
				ms.getSqlCommandType().name());
		DynamicDataSourceHolder.setDbType(lookupKey);
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		if (target instanceof Executor) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		}
	}
	@Override
	public void setProperties(Properties properties) {

	}
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值