详细分析Java中@DS注解,用于切换数据源

前言

Java项目中多个数据源,相应配置拿些方法哪些类访问
类似JDBC每个类都要写一遍会比较冗余,有没有集中式管理呢??

看这篇文章之前推荐阅读:

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. jdbc从入门到精通(全)

1. 基本知识

本身该注解通过Java的切面,源于Spring,推荐阅读补充知识:Spring框架从入门到学精(全)

自定义 @DS 注解通常是为了在方法级别上指定数据源,实现动态数据源切换的功能

通过在不同的方法上标注不同的数据源,可以使得这些方法在执行时使用不同的数据库连接

作用:

  • 实现动态数据源切换:允许在不同的方法中使用不同的数据库连接
  • 简化配置:通过注解方式,避免了在每个方法中手动切换数据源的麻烦
    使用方式
    定义注解:首先需要定义 @DS 注解,指定它的元注解为 @Target 和 @Retention,以及定义它的属性

查看其源码:

package com.baomidou.dynamic.datasource.annotation;


import java.lang.annotation.*;

/**
 * The core Annotation to switch datasource. It can be annotate at class or method.
 *
 * @author TaoYu Kanyuxia
 * @since 1.0.0
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {

    /**
     * groupName or specific database name or spring SPEL name.
     *
     * @return the database you want to switch
     */
    String value();
}
  • @Target({ElementType.TYPE, ElementType.METHOD}):指定了注解 @DS 可以应用在哪些地方,指定了 @DS 注解可以应用在类和方法上

  • @Retention(RetentionPolicy.RUNTIME):指定了注解 @DS 在程序运行时保留,可以通过反射来访问并解析 @DS 注解的信息

  • @Documented:标记这个注解是应该被 javadoc 工具记录的,当生成 API 文档时,如果一个类用 @Documented 注解修饰,那么它的注解将出现在生成的文档中

  • String value();:属性,返回一个字符串,用于指定要切换到的数据库的名称

2. Demo

使用之前先引入依赖包:

<dependency>
    <groupId>com.github.vlvolad</groupId>
    <artifactId>spring-dynamic-datasource</artifactId>
    <version>${spring-dynamic-datasource.version}</version>
</dependency>

配置数据源:

spring:
  datasource: # 配置数据源
    dynamic: # 数据源配置的一个自定义前缀,这个前缀是用于配置动态数据源,即可以在运行时动态切换数据源
      primary: master   # 指定了默认数据源的名称为 master
      strict: true  # 指定了严格模式,意味着如果在代码中请求了一个不存在的数据源,将抛出异常
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/db_master
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql://localhost:3307/db_slave
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver

并在方法上动态切换数据源:

@Service
public class MyService {

    @DS("master")
    public void method1() {
        // 方法1的实现
    }

    @DS("slave")
    public void method2() {
        // 方法2的实现
    }
}

除了上面用在方法上,也可直接用在类上,对于这个类的波动方法都用于某个数据库

3. 实战

以下实战与上述Demo类似,只不过用在了方法上 (与正常开发一样,只不过在此处添加了该注解)

@Service
@DS("slave")
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public class TemperatureAlarmHistServiceImpl implements ITemperatureAlarmHistService {
	@Resource
	private TemperatureAlarmHistMapper temperatureAlarmHistMapper;


	public boolean saveForList(List<TemperatureAlarmHist> temperatureAlarmHistList) {
		return temperatureAlarmHistMapper.saveForList(temperatureAlarmHistList);
	}
}

对应的数据源配置如下:

#数据源配置
spring:
  #  排除DruidDataSourceAutoConfigure
  autoconfigure:
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      datasource:
        master:
           url: jdbc:mysql://localhost:3306/db_master
          username: root
          password: root
        slave:
          url: jdbc:oracle:manong:@//localhost:1521/GIS
          username: root
          password: ROOTGPS1
        ep:
          url: jdbc:sqlserver://localhost:1433;databaseName=manong
          username: root
          password: root

提供的配置中,exclude 属性指定了要排除的自动配置类 com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

这样做的目的是排除Druid连接池的自动配置,避免它与手动配置的数据源发生冲突

另外一种排除的方式:

@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
public class Application {
 
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

前提是 上述这两种方式本身已经引入了该依赖包

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.1</version>
</dependency>
  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
动态数据源切换是指在程序运行时根据需要切换数据源。实现动态数据源切换可以灵活地处理不同数据源的读写操作,提高系统的可扩展性和可维护性。下面介绍一种基于注解的实现方式。 首先,定义一个注解类`@DataSource`,用于标识需要切换数据源: ```java @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource { String value() default "default"; } ``` 其,`value`属性用于指定数据源的名称。如果不指定,则使用默认数据源。 接着,定义一个数据源切换的切面类`DataSourceAspect`,用于切换数据源: ```java @Aspect @Component public class DataSourceAspect { @Pointcut("@within(com.example.datasource.annotation.DataSource) || @annotation(com.example.datasource.annotation.DataSource)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource dataSource = method.getAnnotation(DataSource.class); if (dataSource == null) { dataSource = method.getDeclaringClass().getAnnotation(DataSource.class); } if (dataSource != null) { DynamicDataSourceContextHolder.setDataSource(dataSource.value()); } try { return point.proceed(); } finally { DynamicDataSourceContextHolder.clearDataSource(); } } } ``` 在该切面类,使用`@Pointcut`注解定义一个切点`dataSourcePointCut`,用于匹配所有被`@DataSource`注解标识的类或方法。在`around`方法,通过反射获取方法上的`@DataSource`注解,并根据注解数据源名称切换数据源。在方法执行完毕后,清空数据源上下文。 最后,定义一个数据源上下文类`DynamicDataSourceContextHolder`,用于保存当前数据源的名称: ```java public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSource(String dataSource) { CONTEXT_HOLDER.set(dataSource); } public static String getDataSource() { return CONTEXT_HOLDER.get(); } public static void clearDataSource() { CONTEXT_HOLDER.remove(); } } ``` 在需要切换数据源的类或方法上,添加`@DataSource`注解即可: ```java @Service @DataSource("db1") public class UserServiceImpl implements UserService { @Override public User getUserById(Long id) { return userDao.getUserById(id); } } ``` 上述代码,`UserServiceImpl`类上的`@DataSource("db1")`表示使用名为`db1`的数据源。在调用`getUserById`方法时,切换到`db1`数据源进行操作。 以上就是基于注解实现动态数据源切换的方法。这种实现方式比较简单,但需要手动在需要切换数据源的类或方法上添加注解。如果需要更加自动化地切换数据源,可以考虑使用AOP或动态代理来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农研究僧

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值