前言:
最近新搭建了一个项目,用到了多个数据源,并且需要动态切换,所以写了一个切换数据源的starter包。
spring-boot-starter-multiple-data-source
以读写分离两个数据源举例
一.数据源配置
配置多个数据源
spring:
datasource:
master:
jdbc-url: jdbc:mysql://127.0.0.1:3306/demo1?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8&useSSL=false
username: xxxxxxxxx
password: xxxxxxxxx
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
jdbc-url: jdbc:mysql://127.0.0.1:3306/demo2?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8&useSSL=false
username: xxxxxx
password: xxxxxx
driver-class-name: com.mysql.cj.jdbc.Driver
基础类:
DataSourceType.java 数据源枚举类
public enum DataSourceType {
MASTER("master"),
SLAVE("slave");
private String dataSource;
DataSourceType(String dataSource){
this.dataSource = dataSource;
}
public String getDataSource() {
return dataSource;
}
}
MybatisConfig类:
加载多个数据源,并默认数据源为master
@Configuration
@MapperScan(basePackages = {"**.*Mapper"}) // 扫描DAO
public class MybatisConfig {
@Bean("master")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource master(){
return DataSourceBuilder.create().build();
}
@Bean("slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slave(){
return DataSourceBuilder.create().build();
}
@Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map
上下文类,用于保证线程安全
public class DynamicDataSourceContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal() {
/**
* 将 master 数据源的 key作为默认数据源的 key
*/
@Override
protected String initialValue() {
return DataSourceType.MASTER.getDataSource();
}
};
/**
* 数据源的 key集合,用于切换时判断数据源是否存在
*/
public static List
TargetDataSource.java 注解类
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
/**
* 数据源key值
* @return
*/
DataSourceType value();
}
切面动态切换数据源
@Slf4j
@Aspect
@Order(-1)
@Component
public class DynamicDataSourceAspect {
/**
* 切换数据源
* @param point
* @param targetDataSource
*/
@Before("@annotation(targetDataSource))")
public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {
if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value().getDataSource())) {
log.info("数据源 【{}】 不存在, 使用默认数据源 【{}】 " + targetDataSource.value());
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value().getDataSource());
log.info("数据源变为【" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "】 方法 【" + point.getSignature() + "】");
}
}
/**
* 重置数据源
* @param point
* @param targetDataSource
*/
@After("@annotation(targetDataSource))")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
log.info("重置数据源为 【" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "】 方法为 【" + point.getSignature() + "】");
}
}
这里所有的配置已经完成
应用:
需要在springboot启动类中添加
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
第一种:
在starter包中,添加META-INF/spring.factories文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xxx.xxx.xxx.MybatisConfig
工程引入的jar包会自动加载MybatisConfig文件。
第二种:
在工程中新增 MybatisConfig类 集成starter包中的MybatisConfig
如下代码:
@Configuration
public class MybatisConfig extends xxx.xxx.xxx.MybatisConfig{
}
数据源切换使用,在方法上标识 @TargetDataSource
@TargetDataSource(DataSourceType.SLAVE)
@Override
public void selectById(Integer id) {
//操作slave数据源
}
@TargetDataSource(DataSourceType.MASTER)
@Override
public void updateById(String str) {
//操作master数据源
}
第三种:
这种方式也是springboot目前使用的,不用任何配置,只有注解
新增注解类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MybatisConfig.class)
public @interface MultipleDataSource {
}
然后在启动类上注解
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
@MultipleDataSource//加入这个注解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}