使用spring AbstractRoutingDatasource实现多数据源 public class DynamicDataSource extends AbstractRoutingDataSource { //写数据源 private Object writeDataSource; //读数据源 private Object readDataSource; @Override public void a
public class DynamicDataSource extends AbstractRoutingDataSource {
//写数据源
private Object writeDataSource;
//读数据源
private Object readDataSource;
@Override
public void afterPropertiesSet() {
Assert.isNull(writeDataSource, "Property 'writeDataSource' is required");
setDefaultTargetDataSource(writeDataSource);
Map
读写数据库类型
public enum DBTypeEnum {
WRITE, READ;
}
当前数据库配置上下文
public class DbContextHolder {
private static final ThreadLocal
DATASOURCES = new ThreadLocal
(); private DbContextHolder() { } /** * 设置数据源 * * @param dbType */ public static void setDbType(DBTypeEnum dbType) { DATASOURCES.set(dbType.WRITE); } /* * 获取当前数据源 * @return DBTypeEnum * */ public static DBTypeEnum getDbType() { return DATASOURCES.get(); } /** * 清除上下文数据 */ public static void clearDbType() { DATASOURCES.remove(); } }
自定义事务管理器
public class DynamicDataSourceTransactionManager extends DataSourceTransactionManager {
/**
* 设置数据源
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
boolean readOnly = definition.isReadOnly();
if (readOnly)
DbContextHolder.setDbType(DBTypeEnum.READ);
DbContextHolder.setDbType(DBTypeEnum.WRITE);
super.doBegin(transaction, definition);
}
/**
* 清理本地线程数据源
*/
@Override
protected void doCleanupAfterCompletion(Object transaction) {
super.doCleanupAfterCompletion(transaction);
DbContextHolder.clearDbType();
}
}
mybatis插件(拦截器)
@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 DynamicPlugin implements Interceptor {
private static final Logger _log = LoggerFactory.getLogger(DynamicPlugin.class);
private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";
private static final Map
cachaMap = new HashMap
(); @Override public Object intercept(Invocation invocation) throws Throwable { boolean isSynchronizationActive = TransactionSynchronizationManager.isSynchronizationActive(); if (!isSynchronizationActive) { MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; DBTypeEnum dbTypeEnum = null; if ((dbTypeEnum = cachaMap.get(ms.getId())) == null) { //读方法 if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) { //!selectKeu为自增id查询主键(SELECT LAST_INSERT_ID)方法,使用主库 if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) dbTypeEnum = DBTypeEnum.WRITE; else { BoundSql boundSql = ms.getSqlSource().getBoundSql(invocation.getArgs()[1]); String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " "); if (sql.matches(REGEX)) dbTypeEnum = DBTypeEnum.WRITE; dbTypeEnum = DBTypeEnum.READ; } } else { dbTypeEnum = DBTypeEnum.WRITE; } _log.warn("设置方法[{}] use [{}] Strategy, SqlCommandType [{}]..", ms.getId(), dbTypeEnum.name(), ms.getSqlCommandType().name()); cachaMap.put(ms.getId(), dbTypeEnum); } DbContextHolder.setDbType(dbTypeEnum); } 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) { } }
