🍰SpringBoot下动态数据源切换

第一种:Mybatis-Plus的dynamic-datasource

  Gitee地址:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter

要实现其实很简单,一个注解就可以了

1、创建两个一库,一样的表进行测试

2、搭建SpringBoot引入dynamic-datasource依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.6.1</version>
</dependency>

3、修改SpringBoot配置

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/datasource-1?serverTimezone=UTC
          username: root
          password: xxx
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          url: jdbc:mysql://localhost:3306/datasource-2?serverTimezone=UTC
          username: root
          password: xxx
          driver-class-name: com.mysql.cj.jdbc.Driver

4、在方法上面添加@DS注解

第二种:使用Aop自己实现动态数据源的切换

  Git地址:https://gitee.com/zhang-zhixi/dynamic-datasource-springboot

1、创建两个一库,一样的表进行测试

2、搭建SpringBoot环境

3、自定义切换数据源注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface UsingDataSource {
    String value() default "";
}

4、编写存储自定义数据源的容器

/**
 * @author zhixi
 * 存储数据源的key,使用线程安全方式进行添加数据源
 */
public class DataSourceContextHolder {

    public static ThreadLocal<String> key = new ThreadLocal<>();

    public static void setKey(String key) {
        DataSourceContextHolder.key.set(key);
    }

    public static String getKey() {
        return key.get();
    }

    public static void clearKey() {
        key.remove();
    }
}

5、确定使用的数据源

/**
 * @author zhixi
 * AbstractRoutingDataSource是一个抽象类,是Spring提供的用于动态数据源切换的类。
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 查找哪个数据源的时候使用的key,该方法用于确定当前数据源
     * @return 数据源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getKey();
    }
}

6、重写Spring相关配置

package com.zhixi.config;

import com.zhixi.datasource.DynamicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
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.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhixi
 * Spring相关配置
 */
@Configuration
public class DataSourceConfig {

    /**
     * 为每个数据源单独设置一个Bean
     *
     * @return 数据源1
     */
    @ConfigurationProperties("datasource1")
    @Bean
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 为每个数据源单独设置一个Bean
     *
     * @return 数据源2
     */
    @Bean
    @ConfigurationProperties("datasource2")
    public DataSource dataSource2() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 添加数据源
     *
     * @return 自定义数据源
     */
    @Bean
    public DynamicDataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("ds1", dataSource1());
        targetDataSources.put("ds2", dataSource2());

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 设置目标数据源
        dynamicDataSource.setTargetDataSources(targetDataSources);
        // 设置默认目标数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSource1());
        return dynamicDataSource;
    }

    /**
     * 使用DynamicDataSource作为数据源。
     *
     * @param dynamicDataSource 数据源
     * @return 配置好的SqlSessionFactoryBean对象,以便将其用作MyBatis框架的数据源。
     * @throws IOException 异常
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws IOException {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        /*设置mybatis configuration 扫描路径 */
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //加载配置文件的地址
        bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
        bean.setDataSource(dynamicDataSource);
        return bean;
    }


    /**
     * 使用返回的数据源创建一个DataSourceTransactionManager对象。
     * DataSourceTransactionManager是Spring提供的事务管理器,用于管理基于数据源的事务
     * @return 事务管理器
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }
}

7、使用AOP,在方法调用前设置使用的数据源

package com.zhixi.datasource;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @author zhixi
 * Aop切面,对注解进行切面
 */
@Aspect
@Component
public class DataSourceAspect {
    
    @Pointcut("@annotation(com.zhixi.datasource.UsingDataSource)")
    public void checkPointCut() {

    }

    /**
     * 方法调用之前设置数据源
     * @param usingDataSource 拿到注解的数据源的值
     */
    @Before("checkPointCut() && @annotation(usingDataSource)")
    public void checkBefore(UsingDataSource usingDataSource) {
        // 添加数据源的key
        DataSourceContextHolder.setKey(usingDataSource.value());
    }

    @After("checkPointCut()")
    public void checkAfter() {
        DataSourceContextHolder.clearKey();
    }
}

8、启动类配置

/**
 * @author zhixi
 * exclude = {DataSourceAutoConfiguration.class}:排除SpringBoot自带的数据源配置
 * EnableAspectJAutoProxy:启动动态代理
 */
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@MapperScan("com.zhixi.mapper")
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DynamicDatasourceSpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(DynamicDatasourceSpringbootApplication.class, args);
    }

}

9、编写Controller进行测试

posted @ 2023-02-02 15:02  Java小白的搬砖路  阅读(1297)  评论(0编辑  收藏  举报