一、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.Driverusername: rootpassword: 1234ABcdtype: com.alibaba.druid.pool.DruidDataSourcejdbc-url: jdbc:mysql://192.168.30.132:23306/test1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true&allowMultiQueries=truesecondary:hikari:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 1q2w3e4r5ttype: com.alibaba.druid.pool.DruidDataSourcejdbc-url: jdbc:mysql://192.168.30.136:43306/test2?characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=true&allowMultiQueries=true&zeroDateTimeBehavior=convertToNulldruid:initial-size: 10min-idle: 10max-active: 100max-wait: 60000time-between-eviction-runs-millis: 2000min-evictable-idle-time-millis: 300000validation-query: SELECT 1test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: falsemax-pool-prepared-statement-per-connection-size: 20use-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 {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceNameContextHolder.getDataSourceName();}@Overridepublic 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*/@Beanpublic 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");