DatasourceConfig
package com.tonytaotao.dtsc.common.configuration.datasouce.normal; import com.alibaba.druid.pool.DruidDataSource; import com.tonytaotao.dtsc.common.configuration.datasouce.DBTypeEnum; import com.tonytaotao.dtsc.common.configuration.datasouce.DynamicDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DatasourceConfig { /*------------------------------------------------- 普通多数据源----------------------------------------------*/ @Bean("multipleDataSource") @Primary public DataSource multipleDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map< Object, Object > targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.ONE.getCode(), createDataSourceOne()); targetDataSources.put(DBTypeEnum.TWO.getCode(), createDataSourceTwo()); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(createDataSourceOne()); return dynamicDataSource; } private DataSource createDataSourceOne() { DruidDataSource druidDataSource = createDataSourceBase(); druidDataSource.setUrl("jdbc:mysql://localhost:3306/dtsc?useSSL=false&useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF8&serverTimezone=GMT"); druidDataSource.setUsername("root"); druidDataSource.setPassword("123456"); return druidDataSource; } private DataSource createDataSourceTwo() { DruidDataSource druidDataSource = createDataSourceBase(); druidDataSource.setUrl("jdbc:mysql://localhost:3306/dtsc_1?useSSL=false&useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF8&serverTimezone=GMT"); druidDataSource.setUsername("root"); druidDataSource.setPassword("123456"); return druidDataSource; } private DruidDataSource createDataSourceBase() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); druidDataSource.setInitialSize(50); druidDataSource.setMaxActive(100); druidDataSource.setMinIdle(5); druidDataSource.setMaxWait(1000); druidDataSource.setPoolPreparedStatements(false); druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(-1); druidDataSource.setValidationQuery("SELECT 1"); druidDataSource.setValidationQueryTimeout(1); druidDataSource.setTestOnBorrow(false); druidDataSource.setTestOnReturn(false); druidDataSource.setTestWhileIdle(true); druidDataSource.setTimeBetweenEvictionRunsMillis(60000); druidDataSource.setMinEvictableIdleTimeMillis(30000); return druidDataSource; } }
MybatisPlusConfig
package com.tonytaotao.dtsc.common.configuration.datasouce.normal; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; import java.io.IOException; @Configuration @MapperScan({"com.tonytaotao.dtsc.*.mapper"}) public class MybatisPlusConfig { /** * 性能分析插件 * @return */ @Bean @Profile({"dev","sit"}) public PerformanceInterceptor performanceInterceptor() { return new PerformanceInterceptor(); } /** * 分页插件 * @return */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } /** * * @return */ @Bean("sqlSessionFactory") public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("multipleDataSource") DataSource multipleDataSource) { try { MybatisSqlSessionFactoryBean sqlSessionFactoryBean = mybatisSqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(multipleDataSource); return sqlSessionFactoryBean; } catch (IOException e) { e.printStackTrace(); return null; } } private MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean() throws IOException{ MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); mybatisSqlSessionFactoryBean.setGlobalConfig(globalConfig()); mybatisSqlSessionFactoryBean.setConfiguration(mybatisConfiguration()); mybatisSqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/tonytaotao/dtsc/*/mapper/xml/*Mapper.xml")); mybatisSqlSessionFactoryBean.setTypeAliasesPackage("com.tonytaotao.dtsc.*.entity"); return mybatisSqlSessionFactoryBean; } private GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig(); dbConfig.setIdType(IdType.AUTO); dbConfig.setDbType(DbType.MYSQL); dbConfig.setFieldStrategy(FieldStrategy.NOT_EMPTY); dbConfig.setTableUnderline(true); dbConfig.setLogicDeleteValue("0"); dbConfig.setLogicNotDeleteValue("1"); globalConfig.setDbConfig(dbConfig); LogicSqlInjector logicSqlInjector = new LogicSqlInjector(); globalConfig.setSqlInjector(logicSqlInjector); return globalConfig; } private MybatisConfiguration mybatisConfiguration() { MybatisConfiguration mybatisConfiguration = new MybatisConfiguration(); // 源码里面如果有configuration, 不会注入BaseMapper 里面的方法, 所以这里要这样写 mybatisConfiguration.init(globalConfig()); mybatisConfiguration.setMapUnderscoreToCamelCase(true); mybatisConfiguration.setCacheEnabled(false); return mybatisConfiguration; } }
TransationConfig
package com.tonytaotao.dtsc.common.configuration.datasouce.normal; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; /** * @author wujintao * * Configuration 类似于spring 配置文件,负责注册bean * ComponentScan 注解类查找规则定义 <context:component-scan/> * EnableAspectJAutoProxy 激活Aspect 自动代理 <aop:aspectj-autoproxy/> * proxy-target-class : 默认为false 表示使用JDK 动态代理。如果实现了至少一个接口,Spring 会优先选择使用JDK 动态代理,若果没有实现任何接口,则spring 会选择CGLIB 动态代理,强制使用CGLIB 动态代理,使用以下配置 * exposeProxy : springAOP 只会拦截public 方法,不会拦截provided 和private 方法,并且不会拦截public 方法内部调用的其他方法,也就是说只会拦截代理对象的方法,即增强的是代理对象,而不是原对象, 设置就可以暴露出代理对象,拦截器会获取代理对象,并且将代理对象转换成原对象。从而对内部调用的方法进行增强 * EnableTransactionManagement 启用注解式事务管理 <tx:annotation-driven /> */ @Configuration @EnableTransactionManagement @EnableAspectJAutoProxy(exposeProxy = true) public class TransationConfig { //@Bean public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("multipleDataSource") DataSource multipleDataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(multipleDataSource); return transactionManager; } }
DBTypeEnum
package com.tonytaotao.dtsc.common.configuration.datasouce; import lombok.Getter; public enum DBTypeEnum { ONE("ONE", "db one"), TWO("TWO", "db two"); DBTypeEnum(String code, String msg) { this.code = code; this.msg = msg; } @Getter private String code; private String msg; }
DBAnnotation
package com.tonytaotao.dtsc.common.configuration.datasouce; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DBAnnotation { DBTypeEnum dbSource() default DBTypeEnum.ONE; }
DBAspect
package com.tonytaotao.dtsc.common.configuration.datasouce; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Component @Aspect @Order(-100) @Slf4j public class DBAspect { @Pointcut("execution(* com.tonytaotao.dtsc.*.service..*.*(..))") private void pointcut() { } @Before("pointcut()") public void before(JoinPoint joinPoint) { Object target = joinPoint.getTarget(); String methodName = joinPoint.getSignature().getName(); Class<?> clazz = target.getClass(); log.info("before [ class = {}, method = {} ] execute", clazz.getName(), methodName); Class<?>[] parameterTypes = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes(); try { Method method = clazz.getMethod(methodName, parameterTypes); if (method != null) { if (method.getDeclaringClass().isAnnotationPresent(DBAnnotation.class) && !method.isAnnotationPresent(DBAnnotation.class)) { DBAnnotation dbAnnotation = method.getDeclaringClass().getAnnotation(DBAnnotation.class); DBContextHolder.setDbType(dbAnnotation.dbSource()); log.info("[class = {}, method = {} ] 切换类级数据源 [ {} ] 成功", clazz.getName(), methodName, dbAnnotation.dbSource()); } else if ( method.isAnnotationPresent(DBAnnotation.class)) { DBAnnotation dbAnnotation = method.getAnnotation(DBAnnotation.class); DBContextHolder.setDbType(dbAnnotation.dbSource()); log.info("[class = {}, method = {} ] 切换方法级数据源 [ {} ] 成功", clazz.getName(), methodName, dbAnnotation.dbSource()); } else { DBContextHolder.setDbType(DBTypeEnum.ONE); log.info("[class = {}, method = {} ] 切换默认数据源 [ {} ] 成功", clazz.getName(), methodName, DBTypeEnum.ONE); } } } catch (Exception e) { log.error(" 数据源切换异常", e); } } }
DBContextHolder
package com.tonytaotao.dtsc.common.configuration.datasouce; public class DBContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal<>(); /** * 设置数据源 * @param dbTypeEnum */ public static void setDbType(DBTypeEnum dbTypeEnum) { contextHolder.set(dbTypeEnum.getCode()); } /** * 取得当前数据源 * @return */ public static String getDbType() { return (String) contextHolder.get(); } /** * 清除上下文数据 */ public static void clearDbType() { contextHolder.remove(); } }
DynamicDataSource
package com.tonytaotao.dtsc.common.configuration.datasouce; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DBContextHolder.getDbType(); } }
使用方式
只要在 service 实现类的方法上或者类上加上 @DBAnnotation 指定数据源,就能实现自动切换数据源