SpringBoot多数据源配置:动态切换与分布式事务处理指南

博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
DeepSeek-行业融合之万象视界(附实战案例详解100+)
全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
感兴趣的可以先收藏起来,希望帮助更多的人
在这里插入图片描述

SpringBoot多数据源配置:动态切换与分布式事务处理指南

一、引言

在现代的软件开发中,一个应用系统往往需要与多个数据源进行交互。例如,为了实现读写分离,我们会将读操作和写操作分别指向不同的数据库;或者在进行微服务架构设计时,不同的服务可能会使用不同的数据库。Spring Boot作为一款流行的Java开发框架,为我们提供了便捷的多数据源配置方式。同时,在涉及多个数据源的操作时,如何进行动态切换以及处理分布式事务是我们需要解决的重要问题。本文将详细介绍Spring Boot中多数据源的配置、动态切换以及分布式事务的处理方法。

二、Spring Boot多数据源配置基础

2.1 配置文件设置

首先,我们需要在application.propertiesapplication.yml文件中配置多个数据源的信息。以下是一个application.yml的示例:

spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/db1
      username: root
      password: password1
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      url: jdbc:mysql://localhost:3306/db2
      username: root
      password: password2
      driver-class-name: com.mysql.cj.jdbc.Driver

2.2 数据源配置类

接下来,我们需要创建数据源配置类,将配置文件中的数据源信息加载到Spring容器中。以下是一个简单的数据源配置类示例:

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }
}

在上述代码中,我们使用@Primary注解标记了主数据源,同时使用@ConfigurationProperties注解将配置文件中的数据源信息绑定到数据源对象上。

三、动态数据源切换

3.1 动态数据源上下文管理

为了实现动态数据源的切换,我们需要创建一个动态数据源上下文管理器,用于保存当前线程使用的数据源信息。以下是一个简单的动态数据源上下文管理器示例:

public class DynamicDataSourceContextHolder {

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

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

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

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

3.2 动态数据源实现

接下来,我们需要创建一个动态数据源类,继承自AbstractRoutingDataSource,并重写determineCurrentLookupKey方法,用于根据当前线程的数据源信息动态选择数据源。以下是一个简单的动态数据源实现示例:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.Map;

public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSource();
    }
}

3.3 配置动态数据源

在数据源配置类中,我们需要将动态数据源配置到Spring容器中。以下是修改后的数据源配置类示例:

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
                                        @Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("primary", primaryDataSource);
        targetDataSources.put("secondary", secondaryDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource(primaryDataSource, targetDataSources);
        return dynamicDataSource;
    }
}

3.4 使用动态数据源

在需要切换数据源的地方,我们可以使用DynamicDataSourceContextHolder来设置当前线程使用的数据源。以下是一个简单的示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insertUserToPrimary() {
        DynamicDataSourceContextHolder.setDataSource("primary");
        jdbcTemplate.update("INSERT INTO users (name) VALUES ('John')");
        DynamicDataSourceContextHolder.clearDataSource();
    }

    public void insertUserToSecondary() {
        DynamicDataSourceContextHolder.setDataSource("secondary");
        jdbcTemplate.update("INSERT INTO users (name) VALUES ('Jane')");
        DynamicDataSourceContextHolder.clearDataSource();
    }
}

四、分布式事务处理

4.1 分布式事务概述

在涉及多个数据源的操作时,我们需要确保这些操作的原子性,即要么全部成功,要么全部失败。这就需要使用分布式事务处理机制。常见的分布式事务解决方案有两阶段提交(2PC)、三阶段提交(3PC)、TCC(Try-Confirm-Cancel)和基于消息的最终一致性等。

4.2 使用Seata处理分布式事务

Seata是一个开源的分布式事务解决方案,提供了AT、TCC、SAGA和XA四种事务模式。以下是使用Seata的AT模式处理分布式事务的步骤:

4.2.1 引入依赖

pom.xml文件中引入Seata相关依赖:

<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-seataartifactId>
    <version>2.2.6.RELEASEversion>
dependency>
4.2.2 配置Seata

application.yml文件中配置Seata相关信息:

seata:
  enabled: true
  application-id: my-application
  tx-service-group: my_tx_group
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:
4.2.3 创建全局事务管理器

创建一个全局事务管理器配置类:

import io.seata.spring.annotation.GlobalTransactionScanner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SeataConfig {

    @Bean
    public GlobalTransactionScanner globalTransactionScanner() {
        return new GlobalTransactionScanner("my-application", "my_tx_group");
    }
}
4.2.4 使用全局事务注解

在需要使用分布式事务的方法上添加@GlobalTransactional注解:

import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DistributedTransactionService {

    @Autowired
    private UserService userService;

    @GlobalTransactional
    public void insertUsers() {
        userService.insertUserToPrimary();
        userService.insertUserToSecondary();
    }
}

五、总结

通过本文的介绍,我们了解了Spring Boot中多数据源的配置方法、动态数据源的切换实现以及分布式事务的处理方案。在实际开发中,我们可以根据具体的业务需求选择合适的数据源配置和分布式事务处理方式。同时,我们还需要注意数据源的性能优化和分布式事务的异常处理,以确保系统的稳定性和可靠性。

你可能感兴趣的:(Web,spring,boot,分布式,后端)