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日

相关文章

  • 在html页面中取得session中的值的方法

    获取session值的方法依赖于后端语言的不同,以下以常用的PHP和Java为例,讲解如何在HTML页面中取得session中的值。 使用PHP获取session值 在PHP中,使用session_start()函数开启会话,并且可以使用$_SESSION全局数组保存和取得session中的值。 获取session值的步骤 在需要使用session的页面开头…

    Java 2023年6月15日
    00
  • Java Excel数据导入数据库的方法

    下面是详细的“Java Excel数据导入数据库的方法”的攻略: 准备工作 在进行Java Excel数据导入数据库的操作之前,需要先安装以下软件: Java SE Development Kit (JDK):Java开发套件,提供Java编程所需的基本工具。 Apache POI:Java操作Office文件的API,提供对Excel、Word和Power…

    Java 2023年5月20日
    00
  • SpringBoot启动流程入口参数创建对象源码分析

    Spring Boot启动流程入口参数创建对象源码分析 Spring Boot启动流程中,入口参数创建对象是非常重要的一步。在这一步中,Spring Boot会根据用户的配置信息创建一个Spring应用程序上下文,并将其用于后续的应用程序初始化和启动。以下是Spring Boot启动流程入口参数创建对象的详细攻略: 创建SpringApplication对象…

    Java 2023年5月15日
    00
  • java调用shell命令并获取执行结果的示例

    下面是详细讲解“java调用shell命令并获取执行结果的示例”的完整攻略。 1. Java如何调用Shell命令 在Java中执行Shell命令,可以使用Runtime或ProcessBuilder类。 1.1 使用Runtime类调用Shell命令 String command = "ls -l"; Runtime runtime =…

    Java 2023年5月26日
    00
  • HashMap和HashTable底层原理以及常见面试题

    HashMap和HashTable底层原理以及常见面试题 1. HashMap和HashTable的区别 HashMap和HashTable都是Java中的重要容器类,它们的目的是为了存放和访问键值对。虽然它们的功能是相似的,但是它们在底层的实现和使用上有很大的不同。 1.1 HashMap HashMap的底层是基于哈希表实现的,其键值对存储在Entry数…

    Java 2023年5月26日
    00
  • Java自定义函数调用方法解析

    Java自定义函数调用方法解析 在Java中,可以使用自定义函数实现对某些操作的封装,实现代码复用和简化调用。自定义函数的调用方法与Java内置函数的调用方法略有不同,需要注意以下几个方面。 一、函数定义 Java自定义函数的定义需要指定函数名和参数列表,可以有返回值也可以没有。 下面是一个无参数、无返回值的函数定义示例: public static voi…

    Java 2023年5月26日
    00
  • 详解JAVAEE——SSH三大框架整合(spring+struts2+hibernate)

    下面是“详解JAVAEE——SSH三大框架整合(spring+struts2+hibernate)”的完整攻略,并列举两条示例。 SSH框架整合方法详解 准备工作 首先,你需要准备好以下环境和工具: JDK 1.8+,建议使用最新的版本; Eclipse、IntelliJ IDEA等Java IDE; Maven3+,用于项目的构建; Tomcat9+,用于…

    Java 2023年5月19日
    00
  • PHP和Java的主要区别有哪些?哪个最适合Web开发语言?

    PHP和Java是两种常见的Web开发语言,它们各有优势和适用场景。下面详细讲解它们的主要区别以及哪个更适合Web开发。 PHP和Java的主要区别 语言类型 PHP是一种脚本语言,简单易学,适合快速开发小规模Web应用。Java是一种编译型语言,对程序员的学习和训练更多的关注和考验程序员的编程思想。 性能 Java性能更好,尤其是在处理大量请求时表现更优秀…

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