Spring Boot 动态数据源示例(多数据源自动切换)

下面我将详细讲解 Spring Boot 动态数据源示例(多数据源自动切换)的完整攻略。

什么是动态数据源

动态数据源是一种可以在程序运行时自动切换数据源的技术,它可以在不重启应用的情况下帮助我们实现多数据源的自动切换,非常便于开发和维护。在实践中,我们可以使用 Spring Boot 官方提供的 AbstractRoutingDataSource 类来实现动态数据源的功能。

实现动态数据源

下面,我们来看一下如何使用 Spring Boot 实现动态数据源。

第一步:引入依赖

在 pom 文件中引入以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
</dependencies>

第二步:实现数据源切换逻辑

首先,我们需要继承 AbstractRoutingDataSource 类,并重写它的 determineCurrentLookupKey() 方法,该方法返回的是当前线程的数据源 key。当程序需要切换数据源时,只需要调用 setTargetDataSource() 方法切换到指定的数据源即可。

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceKey();
    }

    @Override
    public void setTargetDataSource(Object targetDataSource) {
        super.setTargetDataSource(targetDataSource);
        afterPropertiesSet();
    }
}

第三步:实现数据源上下文

在动态数据源中,需要为每个线程分配一个数据源,我们可以使用 ThreadLocal 来实现。

public class DataSourceContextHolder {

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

    public static void setDataSourceKey(String dataSourceKey) {
        CONTEXT_HOLDER.set(dataSourceKey);
    }

    public static String getDataSourceKey() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }

}

第四步:实现数据源切换注解

我们可以使用自定义注解来标注使用哪个数据源,这样可以方便地在代码中切换数据源。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    String value() default "";

}

第五步:配置数据源

application.yml 中配置数据源信息:

spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/db_master?useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: password
      driver-class-name: com.mysql.jdbc.Driver
    slave:
      url: jdbc:mysql://localhost:3306/db_slave?useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: password
      driver-class-name: com.mysql.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource

第六步:配置数据源切换拦截器

我们需要配置一个切面来拦截 @DataSource 注解,并将数据源的 key 设置为当前线程的上下文中。

@Aspect
@Component
public class DataSourceAspect {

    @Around("@annotation(dataSource)")
    public Object around(ProceedingJoinPoint joinPoint, DataSource dataSource) throws Throwable {
        try {
            String dataSourceKey = dataSource.value();
            if (StringUtils.isNotEmpty(dataSourceKey)) {
                DataSourceContextHolder.setDataSourceKey(dataSourceKey);
            }

            return joinPoint.proceed();
        } finally {
            DataSourceContextHolder.clearDataSourceKey();
        }
    }

}

至此,我们就实现了动态数据源的功能。

示例

示例一:基础数据源

我们先定义一个简单的数据库表,然后通过基础数据源连接数据库,并进行查询操作。

首先,我们在 master 数据库中创建 user 表:

CREATE TABLE user (
    id BIGINT(20) PRIMARY KEY NOT NULL,
    username VARCHAR(50) NOT NULL,
    password VARCHAR(50) NOT NULL
);

接下来,我们编写一个查询方法:

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public List<User> findAll() {
        return jdbcTemplate.queryForList("SELECT * FROM user", User.class);
    }

}

最后,我们在 Service 层中调用该查询方法:

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public List<User> findAll() {
        return userDao.findAll();
    }

}

示例二:多数据源

接下来,我们修改上面的示例,使用动态数据源来连接数据库。首先,我们在 slave 数据库中创建 user 表:

CREATE TABLE user (
    id BIGINT(20) PRIMARY KEY NOT NULL,
    username VARCHAR(50) NOT NULL,
    password VARCHAR(50) NOT NULL
);

接下来,我们来定义两个 DataSource,分别对应 masterslave 数据库:

@Configuration
public class DataSourceConfig {

    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

}

然后,我们定义一个 DynamicDataSource,这个类是动态数据源实现的关键。

@Configuration
public class DynamicDataSourceConfig {

    @Bean
    @Primary
    public DataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put("masterDataSource", masterDataSource);
        targetDataSources.put("slaveDataSource", slaveDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);

        return dynamicDataSource;
    }

}

然后,我们修改一下 UserDaoUserService 中的代码,使用 @DataSource 来指定数据源。

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @DataSource("masterDataSource")
    public List<User> findAllInMaster() {
        return jdbcTemplate.queryForList("SELECT * FROM user", User.class);
    }

    @DataSource("slaveDataSource")
    public List<User> findAllInSlave() {
        return jdbcTemplate.queryForList("SELECT * FROM user", User.class);
    }

}

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public List<User> findAllInMaster() {
        return userDao.findAllInMaster();
    }

    public List<User> findAllInSlave() {
        return userDao.findAllInSlave();
    }

}

在上面的代码中,我们在 UserDao 中定义了两个查询方法,分别使用了 @DataSource("masterDataSource")@DataSource("slaveDataSource") 注解,用来指定数据源。然后,在 UserService 中调用这两个查询方法,以便在 Controller 中测试使用动态数据源的效果。

最后,我们来测试一下是否可以动态切换数据源。我们定义一个 UserController

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users/master")
    public List<User> getAllUsersInMaster() {
        return userService.findAllInMaster();
    }

    @GetMapping("/users/slave")
    public List<User> getAllUsersInSlave() {
        return userService.findAllInSlave();
    }

}

在上面的代码中,我们定义了两个 HTTP 接口,分别对应查询 masterslave 数据库中的数据。我们在浏览器中访问这两个接口,可以发现数据来源确实是 master 和 slave 数据库,动态数据源切换成功。

至此,我们就完成了 Spring Boot 动态数据源示例的演示。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot 动态数据源示例(多数据源自动切换) - Python技术站

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

相关文章

  • SpringBoot整合Shiro两种方式(总结)

    Spring Boot整合Shiro两种方式(总结) Shiro是一个流行的Java安全框架,可以提供身份验证、授权、加密等功能。Spring Boot可以很方便地与Shiro集成,本文将介绍两种Spring Boot整合Shiro的方式,并提供两个示例,演示如何使用Spring Boot整合Shiro。 1. 方式一:使用Shiro-Spring Boot…

    Java 2023年5月14日
    00
  • Java常用类String的面试题汇总(java面试题)

    下面是整理Java常用类String的面试题汇总的详细攻略。 1. String类的概述 String类是Java中常用的类之一,是由JDK提供的一个不可变的final类,用于存储字符串数据,可以进行字符串的操作和处理。 2. 常见的String类面试题 2.1 如何比较两个字符串是否相等? 首先要了解的是,Java中有两种比较方式,一种是基本类型的比较(=…

    Java 2023年5月20日
    00
  • Java运行时环境之ClassLoader类加载机制详解

    Java运行时环境之ClassLoader类加载机制详解 1. 背景 在Java程序运行过程中,Java虚拟机会将Java程序的.class字节码文件加载进内存中执行。然而,如果所有的.class文件都加载进内存,会导致内存占用过高,因此Java采用了ClassLoader类加载机制,只有在需要使用某个Class时才会动态加载进内存。本文将详细讲解Class…

    Java 2023年5月26日
    00
  • windows下使用 intellij idea 编译 kafka 源码环境

    下面是使用 IntelliJ IDEA 编译 Kafka 源码的完整攻略: 1. 前置条件 安装 JDK 1.8 或以上版本 安装 Git 和 Maven 工具 下载 Kafka 源码 2. 导入源码 使用 IntelliJ IDEA 导入 Kafka 源码,可以通过如下步骤操作:- 打开 IntelliJ IDEA,点击 File -> New -&…

    Java 2023年5月20日
    00
  • Java滚动数组计算编辑距离操作示例

    下面是“Java滚动数组计算编辑距离操作示例”的完整攻略: 什么是编辑距离 编辑距离是指在计算两个字符串相似度时需要进行的操作数。这些操作包括插入、删除、替换等。编辑距离越小,两个字符串的相似度就越高。 算法原理 计算编辑距离的算法有很多种,其中比较常用的是动态规划算法。该算法采用一个二维数组存储每个子问题的最优解,通过填充此数组来求得整个问题的最优解。 由…

    Java 2023年5月26日
    00
  • 关于Java中数组切片的几种方法(获取数组元素)

    首先来讲一下什么是数组切片。在Java中,数组是一组相同类型的数据所组成的有序集合。数组切片指的是从一个数组中截取一个区间来创建一个新的数组。 获取数组元素,即获取数组中的一部分元素。下面将介绍几种Java中获取数组元素的方法。 1. 直接用”[]”操作符 可以使用下标操作符”[]”来获取数组中的某个位置上的元素,例如: int[] arr = {1, 2,…

    Java 2023年5月26日
    00
  • Maven pom.xml与settings.xml详解

    Maven是一个流行的Java构建工具,是基于项目对象模型(Project Object Model, POM)进行构建的。POM是一个XML文件,描述了项目的依赖关系、构建环境、代码目录、打包、部署等信息。POM通过继承机制实现了依赖管理和构建配置的复用,是Maven强大的特性之一。而settings.xml是Maven的配置文件,它包含了Maven的配置…

    Java 2023年5月20日
    00
  • 详解如何在Spring Boot启动后执行指定代码

    在Spring Boot启动后执行指定代码可以使用Spring Boot提供的ApplicationRunner和CommandLineRunner接口。这两个接口都是在Spring Boot应用程序启动完成后运行的回调,并且被称为Spring Boot应用程序的启动回调。 ApplicationRunner接口 ApplicationRunner接口中包含…

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