- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.log4j.Logger;
-
- public class RoutingDataSourceHolder {
- private static Logger logger = LogManager.getLogger();
-
- private static final ThreadLocal
dataSources = new ThreadLocal<>(); -
- //一个事务内用同一个数据源
- public static void setDataSource(String dataSourceName) {
- if (dataSources.get() == null) {
- dataSources.set(dataSourceName);
- logger.info("设置数据源:{}", dataSourceName);
- }
- }
-
- public static String getDataSource() {
- return dataSources.get();
- }
-
- public static void clearDataSource() {
- dataSources.remove();
- }
- }
代码设置了一个事务内使用同一个数据源。
- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.log4j.Logger;
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
-
- public class RoutingDataSource extends AbstractRoutingDataSource {
- private Logger logger = LogManager.getLogger();
-
- @Override
- protected Object determineCurrentLookupKey() {
- String dataSource = RoutingDataSourceHolder.getDataSource();
- logger.info("使用数据源:{}", dataSource);
- return dataSource;
- }
- }
重写 determineCurrentLookupKey方法,返回要使用的数据源key值。
以上两个类解决了动态数据源key值的问题,下面处理初始化targetDataSources对象。
- import com.alibaba.druid.pool.DruidDataSource;
- import com.custom.common.utils.StringUtils;
- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.log4j.Logger;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.DependsOn;
- import org.springframework.context.annotation.Primary;
-
- import javax.sql.DataSource;
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * 配置主从数据库
- */
- @Configuration
- public class DataSourceConfigurer {
- private Logger logger = LogManager.getLogger();
-
- public final static String MASTER_DATASOURCE = "masterDataSource";
- public final static String SLAVE_DATASOURCE = "slaveDataSource";
-
- @Bean(MASTER_DATASOURCE)
- @ConfigurationProperties(prefix = "spring.datasource")
- public DruidDataSource masterDataSource(DataSourceProperties properties) {
- DruidDataSource build = properties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
- logger.info("配置主数据库:{}", build);
- return build;
- }
-
- @Bean(SLAVE_DATASOURCE)
- @ConfigurationProperties(prefix = "spring.slave-datasource")
- public DruidDataSource slaveDataSource(DataSourceProperties properties) {
- DruidDataSource build = properties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
- logger.info("配置从数据库:{}", build);
- return build;
- }
-
- /**
- * Primary 优先使用该Bean
- * DependsOn 先执行主从数据库的配置
- * Qualifier 指定使用哪个Bean
- *
- * @param masterDataSource
- * @param slaveDataSource
- * @return
- */
- @Bean
- @Primary
- @DependsOn(value = {MASTER_DATASOURCE, SLAVE_DATASOURCE})
- public DataSource routingDataSource(@Qualifier(MASTER_DATASOURCE) DruidDataSource masterDataSource,
- @Qualifier(SLAVE_DATASOURCE) DruidDataSource slaveDataSource) {
- if (StringUtils.isBlank(slaveDataSource.getUrl())) {
- logger.info("没有配置从数据库,默认使用主数据库");
- return masterDataSource;
- }
- Map
- map.put(DataSourceConfigurer.MASTER_DATASOURCE, masterDataSource);
- map.put(DataSourceConfigurer.SLAVE_DATASOURCE, slaveDataSource);
- RoutingDataSource routing = new RoutingDataSource();
- //设置动态数据源
- routing.setTargetDataSources(map);
- //设置默认数据源
- routing.setDefaultTargetDataSource(masterDataSource);
- logger.info("主从数据库配置完成");
- return routing;
- }
- }
设置初始化targetDataSources对象关键代码
Map
- # ----------------------------------------
- # 主数据库
- # ----------------------------------------
- spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
- spring.datasource.url=jdbc:mysql://127.0.0.1:3306/custom?useunicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8
- spring.datasource.username=root
- spring.datasource.password=root
- spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-
- # ----------------------------------------
- # 从数据库
- # ----------------------------------------
- spring.slave-datasource.type=com.alibaba.druid.pool.DruidDataSource
- spring.slave-datasource.url=jdbc:mysql://127.0.0.1:3309/custom?useunicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8
- spring.slave-datasource.username=root
- spring.slave-datasource.password=root
- spring.slave-datasource.driver-class-name=com.mysql.cj.jdbc.Driver
一个配置类处理了targetDataSources对象的初始化.
那问题都处理了,那具体要怎么使用呢,关键就是在事务之前调用RoutingDataSourceHolder.setDataSource()方法就可以了。我们写一个aop实现吧。
- import java.lang.annotation.*;
-
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE, ElementType.METHOD})
- @Documented
- public @interface DataSourceWith {
- String key() default "";
- }
2.DataSourceWithAspect
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.core.annotation.Order;
- import org.springframework.stereotype.Component;
-
- import java.lang.reflect.Method;
-
- @Aspect
- @Order(-1)// 保证该AOP在@Transactional之前运行
- @Component
- public class DataSourceWithAspect {
-
- /**
- * 使用DataSourceWith注解就拦截
- */
- @Pointcut("@annotation(com.custom.configure.datasource.DataSourceWith)||@within(com.custom.configure.datasource.DataSourceWith)")
- public void doPointcut() {
-
- }
-
- /**
- * 方法前,为了在事务前设置
- */
- @Before("doPointcut()")
- public void doBefore(JoinPoint joinPoint) {
- MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
- Method method = methodSignature.getMethod();
- // 获取注解对象
- DataSourceWith dataSource = method.getAnnotation(DataSourceWith.class);
- if (dataSource == null) {
- //方法没有就获取类上的
- dataSource = method.getDeclaringClass().getAnnotation(DataSourceWith.class);
- }
- String key = dataSource.key();
- RoutingDataSourceHolder.setDataSource(key);
- }
-
- @After("doPointcut()")
- public void doAfter(JoinPoint joinPoint) {
- RoutingDataSourceHolder.clearDataSource();
- }
-
- }
@DataSourceWith在方法上或者类上都可以。
- /**
- * 获取部门列表
- **/
- @DataSourceWith(key = DataSourceConfigurer.SLAVE_DATASOURCE)
- public List
findDeptTree() { - logger.debug("获取部门树数据");
- List
deptList = new ArrayList<>(); - return deptList;
- }
结果:动态切换成功
原文链接:https://blog.csdn.net/m0_68615056/article/details/123738282