最近的项目中在数据库优化的时候需要用到读写分离,由于代码已经写好了,所以最优的方式就是在代码端不做任何修改,由于mybatis的灵活性,所以考虑用mybatis执行的时候通过插件的形式进行动态设置数据源。又由于spring支持数据源的懒加载和路由数据源的功能,所以最终解决方案是mybatis+spring数据源懒加载+spring动态数据源
话不多说,直接上代码
首先根据写一个动态数据源类去继承spring的AbstractRoutingDataSource类,重写他的determineCurrentLookupKey方法,从AbstractRoutingDataSource类中的
/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
所以这个方法主要是获取数据源的key
public class DynamicDataSource extends AbstractRoutingDataSource {
/*
* (non-Javadoc)
* @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
*/
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDbType();
}
}
public class DBContextHolder {
private static Logger logger = Logger.getLogger(DBContextHolder.class);
/**
* 线程ThreadLocal
*/
private static ThreadLocal contextHolder = new ThreadLocal<>();
public static final String DB_TYPE_WR = "dbTypeWR";
public static final String DB_TYPE_RD = "dbTypeRD";
public static String getDbType() {
String db = contextHolder.get();
if(db == null) {
db = DB_TYPE_WR;// 默认是读写库
}
return db;
}
/**
* @Title: setDbType
* @Description: 设置本线程的dbtype
* @author YiXiaoGang
* @date 2015年12月25日 下午3:04:49
* @param str
*/
public static void setDbType(String str) {
logger.debug("所使用数据源为:" + str);
contextHolder.set(str);
}
/**
* @Title: clearDBType
* @Description: 清理连接类型
* @author YiXiaoGang
* @date 2015年12月25日 下午3:05:00
*/
public static void clearDBType() {
contextHolder.remove();
}
}
@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 DatasouceInterceptor implements Interceptor {
/**
* 插件方法更新
*/
private static final String UPDATE = "update";
/**
* 插件方法查询
*/
private static final String QUERY = "query";
Logger logger = Logger.getLogger(PagingInterceptor.class);
/**
* SQL方言 可选MySql,Oracle
*/
private String diacect;
/**
* mybatis插件拦截方法,用于分页,加解密
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
String methodName = invocation.getMethod().getName();
MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
if(QUERY.equals(methodName)) {
DBContextHolder.setDbType(DBContextHolder.DB_TYPE_RD);
return doQuery(invocation, fields, coditions);
}else if(UPDATE.equals(methodName)) {
DBContextHolder.setDbType(DBContextHolder.DB_TYPE_WR);
return doUpdate(invocation, fields);
}
return null;
}
最后配置多数据源spring数据源部分如下