Springboot动态切换数据源的具体实现与原理分析

yizhihongxing

下面开始讲解“Springboot动态切换数据源的具体实现与原理分析”的完整攻略。

一. 实现原理分析

1.1. 多数据源的实现方式

在多数据源的实现中,我们不能像单数据源的实现那样,在 application.properties 或 application.yml 中写入数据源的配置信息。我们需要寻找一种实现方式,能够在程序运行期间动态配置数据源信息。

1.2. 动态数据源的实现方式

Springboot 动态切换数据源的实现方式主要依靠AbstractRoutingDataSource。

AbstractRoutingDataSource 是 Spring 中的一个抽象类,可以做到根据某种标识进行切换数据源操作。通过继承 AbstractRoutingDataSource,并重写其中的 determineCurrentLookupKey() 方法,我们可以实现多数据源之间的切换。

1.3. 实现步骤

  1. 在 pom.xml 中添加相关依赖

多数据源的实现首先需要引入多个数据源实现的依赖包。例如,在下面的示例中我们使用了 druid 和 mysql-connector-java 作为我们的两个数据源。

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
  1. 定义数据源枚举类

在多数据源实现的过程中,我们需要通过枚举类的方式来定义多个数据源信息。枚举类中需要包含数据源的相关信息,例如数据库连接信息、数据库用户名、数据库密码等。

public enum DataSourceEnum {
    DS_KEY_MASTER("master", "jdbc:mysql://localhost:3306/test1?characterEncoding=UTF-8&useSSL=false", "root", "root"),
    DS_KEY_SLAVE("slave", "jdbc:mysql://localhost:3306/test2?characterEncoding=UTF-8&useSSL=false", "root", "root");

    private String key;

    private String url;

    private String userName;

    private String password;

    DataSourceEnum(String key, String url, String userName, String password) {
        this.key = key;
        this.url = url;
        this.userName = userName;
        this.password = password;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  1. 编写 AbstractRoutingDataSource 的子类

在编写 AbstractRoutingDataSource 的子类时,我们需要重写其中的 determineCurrentLookupKey() 这个方法。在这个方法中,我们需要根据具体的场景来确定哪个数据源将被返回。

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDataSourceKey();
    }
}
  1. 编写自定义数据源切换工具类

自定义数据源切换工具类用于在程序运行时实现数据源切换。在这个类中,我们主要是编写了一个切换数据源的方法 switchDataSource(),这个方法会将指定的数据源切换为当前的数据源。

public class DataSourceSwitcher {

    private static final ThreadLocal<String> dataSources = new ThreadLocal<>();

    public static void setDataSource(String dataSourceKey) {
        dataSources.set(dataSourceKey);
    }

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

    public static void clearDataSource() {
        dataSources.remove();
    }

}
  1. 编写数据源切换 AOP 拦截器

在进行数据源切换时,我们需要编写一个 AOP 拦截器来切换数据源。在这个拦截器中,我们需要实现在访问数据库之前切换数据源。

@Aspect
@Component
public class DataSourceInterceptor {

    @Pointcut("execution(* com.cxy.demo.springbootdemo.controller..*.*(..))")
    public void dataSourcePointCut() {
    }

    @Before("dataSourcePointCut()")
    public void dataSourceBeforeHandle(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> targetClass = joinPoint.getTarget().getClass();
        Method method = signature.getMethod();
        DataSource dataSourceAnno = method.getAnnotation(DataSource.class);
        if (dataSourceAnno == null) {
            dataSourceAnno = AnnotationUtils.findAnnotation(targetClass, DataSource.class);
        }
        if (dataSourceAnno != null) {
            String dsId = dataSourceAnno.value();
            DataSourceSwitcher.setDataSource(dsId);
        }
    }

    @After("dataSourcePointCut()")
    public void after() {
        DataSourceSwitcher.clearDataSource();
    }

}

二. 实现示例

下面是两个简单的示例代码,主要是演示了在程序运行时如何切换数据源。

2.1. 示例 1:在 Service 层中进行数据源的切换

在这个示例中,我们实现了两个简单的查询操作,并且这两个操作所使用的数据源是不同的。具体的实现过程如下:

  1. 首先在 application.properties 中配置数据源相关信息,例如:
spring.datasource.master.url=jdbc:mysql://localhost:3306/test1?characterEncoding=UTF-8&useSSL=false
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.username=root
spring.datasource.master.password=root
spring.datasource.master.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.slave.url=jdbc:mysql://localhost:3306/test2?characterEncoding=UTF-8&useSSL=false
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave.username=root
spring.datasource.slave.password=root
spring.datasource.slave.type=com.alibaba.druid.pool.DruidDataSource
  1. 在 Service 层中调用两个方法,获取两个不同的数据源。
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    @DataSource("master")
    public List<User> getUserListFromMaster() {
        return userMapper.getUserList();
    }

    @Override
    @DataSource("slave")
    public List<User> getUserListFromSlave() {
        return userMapper.getUserList();
    }

}

注意:在这里我们使用了注解 @DataSource 来指定具体的数据源。

  1. 最后,在 Mapper 层中使用 XML 的方式来实现具体的 SQL 操作。

2.2. 示例 2:在 Controller 层中进行数据源的切换

在这个示例中,我们实现一个简单的查询操作,并且我们将数据源的切换放在 Controller 层中进行。具体的实现过程如下:

  1. 在我们的 application.properties 中配置数据源信息。

  2. 在 Controller 层中实现具体的查询操作。在这个操作中,我们需要指定相关的数据源信息。

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/list")
    public List<User> getUserList() {
        DataSourceSwitcher.setDataSource("master");
        List<User> userList = userService.getUserListFromMaster();
        DataSourceSwitcher.clearDataSource();
        return userList;
    }

}

注意:在这里我们使用了类 DataSourceSwitcher来动态控制数据源的切换。在程序运行的过程中,我们可以在需要切换数据源的地方调用 DataSourceSwitcher.setDataSource() 方法来实现数据源的切换。

三. 参考链接

  • SpringBoot实现动态切换数据源(一):https://www.jianshu.com/p/3a3aaff7e6b8
  • SpringBoot实现动态切换数据源(二):https://www.jianshu.com/p/ea413ce69d65
  • Springboot使用druid实现多数据源配置与动态切换:https://www.jianshu.com/p/1878f12ce88f

以上是“Springboot动态切换数据源的具体实现与原理分析”的完整攻略,如果您还有任何问题,欢迎发出跟进提问。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Springboot动态切换数据源的具体实现与原理分析 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • SpringBoot2.x入门教程之引入jdbc模块与JdbcTemplate简单使用方法

    下面我将详细讲解SpringBoot2.x入门教程之引入jdbc模块与JdbcTemplate简单使用方法的完整攻略。 引入jdbc模块 在Spring Boot的Maven配置文件中添加jdbc模块的依赖即可: <dependency> <groupId>org.springframework.boot</groupId&gt…

    Java 2023年5月20日
    00
  • Java8之Lambda表达式使用解读

    Java8之Lambda表达式使用解读 什么是Lambda表达式? Lambda表达式是一种匿名函数,它没有名称,但它有参数列表、函数体和可能存在的返回类型,可以在需要函数类型的上下文中使用。 举个例子,我们可以使用Lambda表达式来实现简化的Runnable接口: Runnable r = () -> System.out.println(&quo…

    Java 2023年5月26日
    00
  • springboot使用hibernate validator校验方式

    下面是关于“Spring Boot使用Hibernate Validator校验方式”的完整攻略,包括使用示例: 1. 什么是Hibernate Validator Hibernate Validator是实现Java Bean Validation规范的一个开源的验证框架。它减少了一些重复的校验代码的编写,并提供了一个标准化的验证方式,可以在不同的Bean…

    Java 2023年5月20日
    00
  • SpringMVC返回图片的几种方式(小结)

    SpringMVC返回图片的几种方式(小结) 在SpringMVC中,我们可以使用多种方式返回图片。本文将介绍三种常用的方式:使用ResponseEntity返回图片、使用@ResponseBody注解返回图片、使用HttpServletResponse输出流返回图片。 使用ResponseEntity返回图片 以下是一个使用ResponseEntity返回…

    Java 2023年5月17日
    00
  • SpringBoot定义优雅全局统一Restful API 响应框架四

    如果没有看前面几篇文章请先看前面几篇 SpringBoot定义优雅全局统一Restful API 响应框架 SpringBoot定义优雅全局统一Restful API 响应框架二 SpringBoot定义优雅全局统一Restful API 响应框架三 目前我们好像似乎解决所有问题,达到了我们理想的效果如下 但是在业务错误返回时候不太理想如下 没有必要返回 r…

    Java 2023年5月10日
    00
  • Maven实现项目构建工具

    Maven是一种基于Java平台的项目管理和构建工具,它可以帮助开发者更加高效,简单地构建、创建和维护项目。在Maven中,你可以定义项目所需的所有依赖关系,指定构建过程中的特定步骤,配置环境变量和创建部署包等。下面是Maven实现项目构建工具的详细攻略。 安装Maven 首先,你需要安装Maven,可以从官方网站 https://maven.apache.…

    Java 2023年5月20日
    00
  • Java线程间的通信方式详解

    Java线程间的通信方式详解 在Java中,线程间的通信是指两个或多个线程之间通过某种方式进行交流、协作的过程,Java线程间的通信主要有以下几种方式: 1.共享内存 共享内存是指多个线程之间共享同一块内存区域,通过修改该内存区域来实现线程之间的通信。Java中的共享内存通信方式有synchronized、volatile、wait和notify等。 示例1…

    Java 2023年5月19日
    00
  • java开发中遇到的异常汇总详解

    Java开发中遇到的常见异常汇总详解 1. 空指针异常 空指针异常是Java开发中最常见的异常之一,它通常发生在尝试调用空对象的方法或访问空对象的成员变量时。 可以通过以下代码来模拟: String str = null; System.out.println(str.length()); 这段代码试图计算空字符串的长度,但由于str为null,因此会抛出空…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部