mybatis service,mapper层动态切换数据源

git地址 https://github.com/xiaogengi/dynamic-data-source.git

首先引入 AOP的jar

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

首先 配置文件 添加数据库基本信息 这里 navigationbar 和 blogcontent 分别是两个数据源

spring:
  datasource:
    navigationbar:
      username: root
      password: Root1234.
      jdbc-url: jdbc:mysql://182.92.236.164:3306/xg-blog-navigation-bar?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver

    blogcontent:
      username: root
      password: Root1234.
      jdbc-url: jdbc:mysql://182.92.236.164:3306/xg-blog-content?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver

继承 AbstractRoutingDataSource 代理类,通过ThreadLocal 在获取当前线程的 数据源key,保证多线程下不会被覆盖

public class DynamicDataSource extends AbstractRoutingDataSource {

    public static final ThreadLocal<String> DATA_SOURCE = new ThreadLocal<>();

    public DynamicDataSource(DataSource dataSource, Map<Object, Object> dataSourceMap){
        // 默认使用的数据源
        super.setDefaultTargetDataSource(dataSource);
        // 多个数据源的map 。  master : jdbc:mysql://182.92.236.164:3306
        //                   salve  : jdbc:mysql://182.92.236.164:3307
        super.setTargetDataSources(dataSourceMap);
        super.afterPropertiesSet();
    }

    // 这个方法返回的是当前线程使用的数据源key
    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }


    public static void setDataSource(String dataSource){
            DATA_SOURCE.set(dataSource);
    }

    public static String getDataSource(){
        return DATA_SOURCE.get();
    }

    public static void removeDataSource(){
        DATA_SOURCE.remove();
    }
}

创建一个枚举 用来存放数据源key

public enum DataSourceEnum {

    NAVIGATION_BAR("navigationbar"),
    BLOG_CONTENT("blogcontent");

   private String key;

    DataSourceEnum(java.lang.String key) {
        this.key = key;
    }

    public String getKey() {
        return key;
    }
}

创建一个配置类, 把多个数据源加载进来,然后统一放入map中

@Configuration
public class DynamicDataSourceConfig {


    @Bean("blogContentDataSource")
    @ConfigurationProperties("spring.datasource.blogcontent")// 把配置文件中这个属性下的值获取到,并创建数据源
    public DataSource blogContentDataSource(){
        return DataSourceBuilder.create().build();
    }


    @Bean("navigationBarDataSource")
    @ConfigurationProperties("spring.datasource.navigationbar")
    public DataSource navigationBarDataSource(){
        return DataSourceBuilder.create().build();
    }



    @Primary // 主数据源,不加会报错
    // 定义 bean name,如果使用了 SqlSessionFactory 则把当前 bean 赋值到数据源中
    @Bean("dataSource")                            
    //@Qualifier DataSource 多个对象,值是对象的beanName
    public DynamicDataSource dataSource(@Qualifier("blogContentDataSource") DataSource blogContentDataSource ,@Qualifier("navigationBarDataSource") DataSource navigationBarDataSource){
        Map<Object, Object> map = new HashMap<>();
        map.put(DataSourceEnum.NAVIGATION_BAR.getKey(), navigationBarDataSource);
        map.put(DataSourceEnum.BLOG_CONTENT.getKey(), blogContentDataSource);
        return new DynamicDataSource(navigationBarDataSource, map);
    }


}

创建一个注解,

@Retention(RetentionPolicy.RUNTIME)// 运行时使用
@Target({ElementType.METHOD})      // 方法级注解
public @interface DS {
    DataSourceEnum name() default DataSourceEnum.NAVIGATION_BAR;
}

创建一个切面类。 通过注解进行切面,获取注解中的数据源key,进行set 放到 ThreadLock中

@Aspect
@Component
@Order(-1)
public class DataSourceAspect {

    @Around("@annotation(com.xg.blog.gateway.datasource.annotation.DS)")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        DS ds = method.getAnnotation(DS.class);
        if(ds == null){
            DynamicDataSource.setDataSource(DataSourceEnum.NAVIGATION_BAR.getKey());
            System.out.println(Thread.currentThread().getName() + ": dataSource is a " + DataSourceEnum.NAVIGATION_BAR.getKey());
        }else{
            DynamicDataSource.setDataSource(ds.name().getKey());
            System.out.println(Thread.currentThread().getName() + ": dataSource is a " + ds.name().getKey());
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSource.removeDataSource();
            System.out.println(Thread.currentThread().getName() + ": remove dataSource");
        }
    }

}

注意!!! 启动类一定要剔除DataSourceAutoConfigurant
然后通过@EnableAspectJAutoProxy 进行启动 AOP

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAspectJAutoProxy
public class XgBlogGatewayApplication {

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

}

service中注解的使用
在这里插入图片描述
源码中 判断是否拓展了数据源,如果有就是用拓展的数据源,否则使用DynamicDataSource构造方法中setDefaultTargatDataSource中的数据源
在这里插入图片描述

service层测试

@Component
public class DataSourceTest {


    @DS(name = DataSourceEnum.BLOG_CONTENT)
    public void createBlogContentDataSource(){
        System.out.println("createBlogContentDataSource ........");
    }


    @DS(name = DataSourceEnum.NAVIGATION_BAR)
    public void createNavigationBarDataSource(){
        System.out.println("createNavigationBarDataSource ........");
    }

}
@SpringBootTest
class XgBlogGatewayApplicationTests {

    @Autowired
    private DataSourceTest dataSourceTest;

    @Test
    public void dataSourceTest(){

        dataSourceTest.createBlogContentDataSource();

        dataSourceTest.createNavigationBarDataSource();


    }

}

在这里插入图片描述
mapper层测试
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于切换数据源,可以使用 MyBatis 提供的动态数据源切换功能,具体实现方式如下: 1. 在配置文件中配置多个数据源,如: ``` <bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db1"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db2"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> ``` 2. 在 MyBatisMapper 接口中定义多个方法,每个方法对应一个数据源,如: ``` public interface UserMapper1 { @Select("select * from user") List<User> getAllUsers(); } public interface UserMapper2 { @Select("select * from user") List<User> getAllUsers(); } ``` 3. 在 Service 中使用动态数据源切换功能,如: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper1 userMapper1; @Autowired private UserMapper2 userMapper2; @Override public List<User> getAllUsers(String dataSource) { if ("dataSource1".equals(dataSource)) { DynamicDataSourceContextHolder.setDataSourceKey("dataSource1"); return userMapper1.getAllUsers(); } else if ("dataSource2".equals(dataSource)) { DynamicDataSourceContextHolder.setDataSourceKey("dataSource2"); return userMapper2.getAllUsers(); } return null; } } ``` 其中,DynamicDataSourceContextHolder 是一个线程安全的数据源上下文管理器,用于存储当前线程使用的数据源 key。在每次访问数据库之前,需要先设置当前线程使用的数据源 key,访问完毕后需要清除当前线程使用的数据源 key。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值