一、yml配置多数据源
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
master:
url: jdbc:mysql://localhost:3306/my_test?characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: root
slave:
url: jdbc:mysql://localhost:3306/my_util?characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: root
initialSize: 5
minIdle: 10
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
maxEvictableIdleTimeMillis: 900000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
allow:
url-pattern: /druid/*
login-username: admin
login-password: 123456
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
commons-log:
connection-logger-name: stat,wall,log4j
二、定义数据源枚举类
public enum DataSourceType {
MASTER,
SLAVE,
}
三、数据源属性的实体类
@Component
public class DataSourceProperties {
@Value("${spring.datasource.druid.initialSize}")
private int initialSize;
@Value("${spring.datasource.druid.minIdle}")
private int minIdle;
@Value("${spring.datasource.druid.maxActive}")
private int maxActive;
@Value("${spring.datasource.druid.maxWait}")
private int maxWait;
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
private int maxEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.druid.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.druid.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.druid.testOnReturn}")
private boolean testOnReturn;
public DruidDataSource dataSource(DruidDataSource datasource) {
datasource.setInitialSize(initialSize);
datasource.setMaxActive(maxActive);
datasource.setMinIdle(minIdle);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
return datasource;
}
}
四、使用ThreadLocal维护当前使用的数据源
@Slf4j
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceType> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSourceType(DataSourceType dsType) {
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
public static DataSourceType getDataSourceType() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
五、继承AbstractRoutingDataSource抽象类
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
六、编写配置类
@Slf4j
@Configuration
@MapperScan(basePackages = {"com.it.mapper"}, sqlSessionTemplateRef = "sqlSessionTemplate")
public class DataSourceConfig {
@Autowired
private DataSourceProperties dataSourceProperties;
@Autowired
private Environment environment;
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource master() {
log.info("初始化{}数据库", DataSourceType.MASTER);
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setName(DataSourceType.MASTER.name());
return dataSourceProperties.dataSource(dataSource);
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
public DataSource slave() {
log.info("初始化{}数据库", DataSourceType.SLAVE);
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setName(DataSourceType.SLAVE.name());
return dataSourceProperties.dataSource(dataSource);
}
@Bean
@Primary
public DynamicDataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(master());
Map<Object, Object> dsMap = new HashMap<>();
dsMap.put(DataSourceType.MASTER, master());
dsMap.put(DataSourceType.SLAVE, slave());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dynamicDataSource);
String locationPattern = environment.getProperty("mybatis.mapper-locations");
Assert.notNull(locationPattern, "[mybatis.mapper-locations] is null");
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(locationPattern));
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
factoryBean.setConfiguration(configuration);
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
七、动态切换数据源注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
DataSourceType value() default DataSourceType.MASTER;
}
八、切面类完成动态切库
@Aspect
@Component
@Order(0)
public class DynamicDataSourceAspect {
@Before("@annotation(com.it.annotation.DataSource)")
public void before(JoinPoint point) {
Class<?> className = point.getTarget().getClass();
String methodName = point.getSignature().getName();
Class<?>[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
DataSourceType dataSource = DataSourceType.MASTER;
try {
Method method = className.getMethod(methodName, argClass);
if (method.isAnnotationPresent(DataSource.class)) {
DataSource annotation = method.getAnnotation(DataSource.class);
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
DataSourceContextHolder.setDataSourceType(dataSource);
}
@After("@annotation(com.it.annotation.DataSource)")
public void after(JoinPoint point) {
DataSourceContextHolder.clearDataSourceType();
}
}
九、注解自动切换数据源示例
@DataSource(DataSourceType.SLAVE)
public List<Student> getAllStudent() {
return studentMapper.selectAll();
}
十、手动切换数据源示例
public List<Student> getAllStudent() {
DataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE);
List<Student> list = studentMapper.selectAll();
DataSourceContextHolder.setDataSourceType(DataSourceType.MASTER);
return list;
}