一、pom引入
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
二、yml文件配置
multiple: true
spring:
datasource:
primary:
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234ABcd
type: com.alibaba.druid.pool.DruidDataSource
jdbc-url: jdbc:mysql://192.168.30.132:23306/test1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true&allowMultiQueries=true
secondary:
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1q2w3e4r5t
type: com.alibaba.druid.pool.DruidDataSource
jdbc-url: jdbc:mysql://192.168.30.136:43306/test2?characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=true&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
druid:
initial-size: 10
min-idle: 10
max-active: 100
max-wait: 60000
time-between-eviction-runs-millis: 2000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
use-global-data-source-stat: true
三、DataSourceName注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface DataSourceName {
/**
* 指定数据源名称
* @return dataSourceName
*/
String value() default "primary";
}
四、DynamicDataSourceAspect
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(dataSourceName)")
public void beforeSwitchDataSource(DataSourceName dataSourceName){
// 切换数据源
DataSourceNameContextHolder.setDataSourceName(dataSourceName.value());
}
@After("@annotation(com.sgcc.dlsc.pxsettelementinfpubquery.config.DB.DataSourceName)")
public void afterSwitchDataSource(){
DataSourceNameContextHolder.resetDataSourceName();
}
}
五、DynamicDataSourceRouter
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.io.PrintWriter;
import java.sql.SQLException;
public class DynamicDataSourceRouter extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceNameContextHolder.getDataSourceName();
}
@Override
public void setLogWriter(PrintWriter pw) throws SQLException {
super.setLogWriter(pw);
}
}
六、DataSourceConfig
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
// 自定义数据源一定要排除SpringBoot自动配置数据源,不然会出现循环引用的问题,The dependencies of some of the beans in the application context form a cycle
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@ConditionalOnProperty(name = "multiple",havingValue = "true")
public class DataSourceConfig {
@Bean(name = "primary")
@ConfigurationProperties(prefix = "spring.datasource.primary.hikari")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondary")
@ConfigurationProperties(prefix = "spring.datasource.secondary.hikari")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源
* 通过AOP+注解实现动态切换
*
* @return
*/
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dataSource() {
DynamicDataSourceRouter dynamicDataSource = new DynamicDataSourceRouter();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());
// 配置多数据源
Map<Object, Object> dataSourceMap = new HashMap(5);
dataSourceMap.put("primary", primaryDataSource());
dataSourceMap.put("secondary", secondaryDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
return dynamicDataSource;
}
/**
* 配置@Transactional注解事物
*
* @return
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
七、DataSourceNameContextHolder
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.NamedThreadLocal;
@Slf4j
public class DataSourceNameContextHolder {
private static final ThreadLocal<String> dataSourceNameContextHolder = new NamedThreadLocal<>("DataSourceContext");
/** 默认数据源名称 */
public static final String DEFAULT_DATASOURCE_NAME = "primary";
public static void setDataSourceName(String dataSourceName) {
log.info("切换到[{}]数据源", dataSourceName);
dataSourceNameContextHolder.set(dataSourceName);
}
public static String getDataSourceName() {
return dataSourceNameContextHolder.get() != null ? dataSourceNameContextHolder.get() : DEFAULT_DATASOURCE_NAME;
}
public static void resetDataSourceName() {
dataSourceNameContextHolder.remove();
}
}
八、切换数据源
DataSourceNameContextHolder.setDataSourceName("secondary");