import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDatabaseType();
}
}
@Data
@Component
public class CommonConfig {
private Integer initialSize = 5;
private Integer maxActive = 30;
private Integer minPoolSize =2;
private Long maxWait = 6000L;
private Integer minIdle =10;
private Long timeBetweenEvictionRunsMillis =60000L;
private Long minEvictableIdleTimeMillis =300000L;
private String validationQuery = "SELECT 1 FROM DUAL";
private Integer validationQueryTimeout = 5;
private Boolean testWhileIdle = true;
private Boolean testOnBorrow = true;
private Boolean testOnReturn = false;
private Integer maxOpenPreparedStatements =20;
private Boolean removeAbandoned = true;
private Integer removeAbandonedTimeout = 1800;
private Boolean logAbandoned =true;
private Boolean poolPreparedStatements =true;
private String filters ="stat";
private Boolean keepAlive = true;
}
@Data
@Component
public class BaseConfig extends CommonConfig {
@Value("${base.driverClassName}")
private String driverClassName;
@Value("${base.url}")
private String url;
@Value("${base.username}")
private String username;
@Value("${base.password}")
private String password;
}
public class DataSourceContextHolder {
/**
* 默认数据源
*/
public static final DataSourceType DEFAULT_DS = DataSourceType.BASE;
private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
public static void setDatabaseType(DataSourceType type) {
contextHolder.set(type);
}
public static DataSourceType getDatabaseType() {
return contextHolder.get();
}
public static void clearDatabaseType() {
contextHolder.remove();
}
}
public enum DataSourceType {
/**
* 基础默认
*/
BASE,
SECOND;
}
/**
* @description:
* @author:
* @date:
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD
})
public @interface DataSource {
DataSourceType value() default DataSourceType.BASE;
}
@Configuration
@Slf4j
public class DataSourceConfiguration {
@Autowired
BaseConfig baseConfig;
@Autowired(required = false)
SecondConfig secondConfig;
/**
* 基础默认数据源
*
* @return
* @throws Exception
*/
@Bean
public DataSource baseDataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(baseConfig.getUrl());
datasource.setUsername(baseConfig.getUsername());
datasource.setPassword(baseConfig.getPassword());
datasource.setDriverClassName(baseConfig.getDriverClassName());
datasource.setInitialSize(baseConfig.getInitialSize());
datasource.setMinIdle(baseConfig.getMinIdle());
datasource.setMaxActive(baseConfig.getMaxActive());
datasource.setMaxWait(baseConfig.getMaxWait());
datasource.setTimeBetweenEvictionRunsMillis(baseConfig.getTimeBetweenEvictionRunsMillis());
datasource.setMinEvictableIdleTimeMillis(baseConfig.getMinEvictableIdleTimeMillis());
datasource.setValidationQuery(baseConfig.getValidationQuery());
//datasource.setValidationQueryTimeout(baseConfig.getValidationQueryTimeout());
datasource.setTestWhileIdle(baseConfig.getTestWhileIdle());
datasource.setTestOnBorrow(baseConfig.getTestOnBorrow());
datasource.setTestOnReturn(baseConfig.getTestOnReturn());
datasource.setPoolPreparedStatements(baseConfig.getPoolPreparedStatements());
datasource.setKeepAlive(baseConfig.getKeepAlive());
log.info("***************成功加载基础数据源***************");
return datasource;
}
@Bean
public DataSource secondDataSource() {
DruidDataSource datasource = new DruidDataSource();
if(taConfig != null){
datasource.setUrl(secondConfig.getUrl());
datasource.setUsername(secondConfig.getUsername());
datasource.setPassword(secondConfig.getPassword());
datasource.setDriverClassName(secondConfig.getDriverClassName());
datasource.setInitialSize(secondConfig.getInitialSize());
datasource.setMinIdle(secondConfig.getMinIdle());
datasource.setMaxActive(secondConfig.getMaxActive());
datasource.setMaxWait(secondConfig.getMaxWait());
datasource.setTimeBetweenEvictionRunsMillis(secondConfig.getTimeBetweenEvictionRunsMillis());
datasource.setMinEvictableIdleTimeMillis(secondConfig.getMinEvictableIdleTimeMillis());
datasource.setValidationQuery(secondConfig.getValidationQuery());
//datasource.setValidationQueryTimeout(secondConfig.getValidationQueryTimeout());
datasource.setTestWhileIdle(secondConfig.getTestWhileIdle());
datasource.setTestOnBorrow(secondConfig.getTestOnBorrow());
datasource.setTestOnReturn(secondConfig.getTestOnReturn());
datasource.setPoolPreparedStatements(secondConfig.getPoolPreparedStatements());
datasource.setKeepAlive(secondConfig.getKeepAlive());
log.info("***************成功加载SECOND数据源***************");
}
return datasource;
}
/**
* @param baseDataSource
* @return
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean
@Primary
public DynamicDataSource dataSource(
@Qualifier("baseDataSource") DataSource baseDataSource,
@Qualifier("secondDataSource") DataSource taDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.BASE, baseDataSource);
targetDataSources.put(DataSourceType.SECOND, taDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
// 该方法是AbstractRoutingDataSource的方法
dataSource.setTargetDataSources(targetDataSources);
// 默认的datasource设置为baseDataSource
dataSource.setDefaultTargetDataSource(baseDataSource);
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(
@Qualifier("baseDataSource") DataSource baseDataSource,
@Qualifier("secondDataSource") DataSource secondDataSource) {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(this.dataSource(baseDataSource,secondDataSource));
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//添加XML目录
try {
fb.setMapperLocations(resolver.getResources("classpath:mapping/*.xml"));
return fb.getObject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Autowired
@Bean(name = "baseJdbcTemplate")
public JdbcTemplate baseJdbcTemplate(@Qualifier("baseDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Autowired
@Bean(name = "secondJdbcTemplate")
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
/**
* 配置事务管理器
*/
@Bean("transactionManager")
public DataSourceTransactionManager transactionManager(@Qualifier("baseDataSource")DataSource baseDataSource) {
return new DataSourceTransactionManager(baseDataSource);
}
@Bean
@Qualifier("secondTransactionManager")
public PlatformTransactionManager secondTransactionManager(@Qualifier("secondDataSource") DataSource secondDataSource) {
return new DataSourceTransactionManager(secondDataSource);
}
}
@Aspect
@Component
@Order(-2)
@Slf4j
public class DynamicDataSourceAspect {
@Pointcut(value = "@annotation(com.xxxxxxx.DataSource)")
private void pointcut() {
}
@Before("pointcut()")
public void doBefore(JoinPoint point) {
DataSourceContextHolder.clearDatabaseType();
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
//设置默认数据源
DataSourceType dataSource = DataSourceContextHolder.DEFAULT_DS;
String methodName = "";
try {
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource annotation = m
.getAnnotation(DataSource.class);
dataSource = annotation.value();
methodName = m.getName();
}
} catch (Exception e) {
log.error("DataSource switch error:{}", e.getMessage(), e);
} finally {
log.info("{} | method: {} | datasource: {} | begin",
((MethodSignature) point.getSignature()).getMethod().getDeclaringClass(), methodName,
dataSource);
}
DataSourceContextHolder.setDatabaseType(dataSource);
}
@After("pointcut()")
public void doAfter(JoinPoint point) {
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
String methodName = "";
Class<?>[] classz = target.getClass().getInterfaces();
try {
Method m = classz[0].getMethod(method, parameterTypes);
methodName = m.getName();
} catch (Exception e) {
log.error("DataSource switch after error:{}", e.getMessage(), e);
}
log.info("{} | method {} | datasource {} | end",
((MethodSignature) point.getSignature()).getMethod().getDeclaringClass(), methodName,
DataSourceContextHolder.getDatabaseType());
DataSourceContextHolder.clearDatabaseType();
}
}
默认数据源不需要加@Datasource注解,使用非默认数据源时需要在mapper方法上增加@Datasource数据源并指定,使AOP切换生效
@DataSource(DataSourceType.SECOND)
String test();