使用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 MapcachaMap = 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) { } }