Spring Boot中动态数据源切换
2020-06-16 本文已影响0人
_小石头啊
Spring Boot中动态数据源切换
运行版本
- spring boot 2.2.5.RELEASE
- 数据源使用spring boot自带的Hikari
- 数据库连接 mybatis plus (spring data jpa也可行)
- 不支持事务
步骤说明:
1.新建spring boot项目引入依赖
<!--添加Web依赖(servlet) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mybatis Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!-- mysql 链接依赖包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- AOP begin 如果是spring data jpa则不用引入这两个包。jpa中已带有 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
<!-- 解决读取配置文件时报错:Spring Boot configuration annotation processor not found in classpath -->
<!-- 导入配置文件处理器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
2.修改application.yaml配置文件
server:
port: 9099
spring:
application:
name: datasource-switch
datasource:
one:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://10.172.246.234:3306/zhouyf?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
two:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://10.172.246.234:3306/zhouyf2?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
three:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://10.172.246.234:3306/zhouyf3?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
#读取mybatis的mapper文件
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
3.创建数据源类,读取配置文件中配置的数据源
@Component
@Data
@ConfigurationProperties(prefix ="spring.datasource")
@EnableConfigurationProperties
public class DBproperties {
private HikariDataSource one;
private HikariDataSource two;
private HikariDataSource three;
}
HikariDataSource 中的HikariConfig为数据源可配置的信息,如有需要可以参考配置在配置文件中
4.创建DynamicDataSource类扩展Spring的AbstractRoutingDataSource抽象类,重写 determineCurrentLookupKey() 方法
/**
* DynamicDataSource扩展Spring的AbstractRoutingDataSource抽象类,重写 determineCurrentLookupKey() 方法
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。
* 也就是说 ThreadLocal 可以为每个线程创建一个【单独的变量副本】,相当于线程的 private static 类型变量。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好
* @param defaultTargetDataSource 默认数据源
* @param targetDataSources 目标数据源
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
4.注册刚创建的实体DynamicDataSource并把数据源信息传进去
@Configuration
public class DynamicDataSourceConfig {
@Autowired
private DBproperties properties;
@Bean
@Primary
public DynamicDataSource dataSource() {
Map targetDataSources =new HashMap<>();
targetDataSources.put("one", properties.getOne());
targetDataSources.put("two", properties.getTwo());
targetDataSources.put("three", properties.getThree());
//设置one为默认数据源
return new DynamicDataSource(properties.getOne(),targetDataSources);
}
}
5.创建注解@UseDataSource并创建该注解的切面
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UseDataSource {
String value() default "";
}
@Slf4j
@Aspect
@Component
public class DataSourceAspect implements Ordered {
@Pointcut("@annotation(zhouyf.datasource.UseDataSource)")
public void dataSourcePointCut() {}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
UseDataSource ds = method.getAnnotation(UseDataSource.class);
if (ds == null) {
//value为空则使用默认数据源
DynamicDataSource.setDataSource("one");
log.warn("----数据源使用----- 当前数据源为:one");
} else {
DynamicDataSource.setDataSource(ds.value());
log.warn("----数据源使用----- 当前数据源为:"+ds.value());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
}
}
@Override
public int getOrder() {
return 1;
}
}
6.测试
数据库信息以及其中的数据image
image
image
spring boot data jpa 使用注意事项
- 如果 jpa.hibernate.ddl-auto为update或great则会在默认的数据源上创建或修改表结构
- 可以返回同一实体类(如果数据源2和数据源1同时都有一张表的情况下,会去切换的数据源下找)