开发者

SpringBoot实现数据源动态切换的最佳姿势

目录
  • 1.1数据库准备
  • 1.2工程环境准备
  • 1.3编写多数据源
  • 1.4利用切面代理类设置数据源
  • 2.1数据库准备
  • 2.2修改全局配置文件
  • 2.3编写相关的服务类
  • 2.4修改动态数据源服务类
  • 2.5修改动态数据源配置类
  • 2.6配置启动时加载数据源服务类
  • 2.7调整 SpringBootApplication 注解配置

在介绍动态数据源之前,我们先一起来看看多数据源在 Spring Boot 中的实现方式。

SpringBoot实现数据源动态切换的最佳姿势

1.1数据库准备

创建两个库,分别是db_test_1db_test_2db_test_1数据库中创建一张用户表,脚本如下:

SpringBoot实现数据源动态切换的最佳姿势

db_test_2数据库中创建另一张账户表,脚本如下:

SpringBoot实现数据源动态切换的最佳姿势

1.2工程环境准备

pom.XML中添加相关的依赖包,示例如下:

<!--spring boot核心-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring boot 测试-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!--mysql 驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-Java</artifactId>
</dependency>
<!-- druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
<!--myBATis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<!--ASPectj 注解代理-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

1.3编写多数据源

SpringBoot实现数据源动态切换的最佳姿势

创建动态数据源服务类

首先,创建一个DynamicDataSource类,并继承AbstractRoutingDataSource抽象类,同时重写determineCurrentLookupKey()方法,代码示例如下:

SpringBoot实现数据源动态切换的最佳姿势

创建动态数据源缓存类

创建一个DataSourceContextHolder类,用于缓存数据源,同时需要确保线程环境下安全

package com.example.dynamic.datasource.config;
 
publicclass DataSourceContextHolder {
 
    /**
     * 设置线程独立变量,用于存储数据源唯一标记
     */
    privatestaticfinal ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();
 
    /**
     * 设置数据源
     * @param dataSourceName 数据源名称
     */
    public static void set(String dataSourceName){
        DATASOURCE_HOLDER.set(dataSourceName);
    }
 
    /**
     * 获取当前线程的数据源
     * @return 数据源名称
     */
    public static String get(){
        return DATASOURCE_HOLDER.get();
    }
 
    /**
     * 删除当前数据源
     */
    public static void remove(){
        DATASOURCE_HOLDER.remove();
    }
 
}

创建动态数据源配置类

接着,创建一个DataSourceConfig配置类,设置动态数据源相关的参数,并注入到 Bean 工厂,代码示例如下:

package com.example.dynamic.datasource.config;
 
@Configuration
publicclass DataSourceConfig {
 
    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.db1.druid")
    public DataSource db1(){
        return DruidDataSourceBuilder.create().build();
    }
 
    @Bean(name = "DB2")
    @ConfigurationProperties(prefix = "spring.datasource.db2.druid")
    public DataSource db2(){
        return Druid编程客栈DataSourceBuilder.create().build();
    }
 
    @Bean
    @Primary
    public DynamicDataSource createDynamicDataSource(){
        //android 配置数据源集合,其中key代表数据源名称,DataSourceContextHolder中缓存的就是这个key
        Map<Object,Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("db1",db1());
        dataSourceMap.put("db2",db2());
 
        // 注入动态数据源到bean工厂
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(db1());
        // 设置动态数据源集
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }
}

编写相关配置变量

根据上面的配置变量,我们还需要在application.properties文件中添加相关的数据源变量,内容如下:

SpringBoot实现数据源动态切换的最佳姿势

排除自动装配数据源

需要在注解@SpringBootApplication类上排除自动装配数据源配置,内容如下:

SpringBoot实现数据源动态切换的最佳姿势

1.4利用切面代理类设置数据源

在上文中,我们采用的是手动方式来设置数据源,在实际的业务开发中,我们通常会采用切面代理类来设置数据源,以便简化代码复杂度。

创建数据源注解

首先,定义一个数据源注解来实现数据源的切换,同时配置一个默认的数据源名称,代码示例如下:

package com.example.dynamic.datasource.config.aop;
 
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DbSource {
 
    /**
     * 数据源key值
     * @return
     */
    String value() default "db1";
}

编写数据源代理类

接着,基于@DbSource注解,创建一个 AOP 代理类,所有配置该注解的方法都会被前后拦截,代码示例如下:

package com.example.dynamic.datasource.config.aop;
 
@Order(1)
@Aspect
@Component
publicclass DbSourceAspect {
 
    privatestaticfinal Logger LOGGER = LoggerFactory.getLogger(DbSourceAspect.class);
 
 
    @Pointcut("@annotation(com.example.dynamic.datasource.config.aop.DbSource)")
    public void dynamicDataSource(){}
 
    @Around("dynamicDataSource()")
    public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {
        // 获取要切换的数据源名称
        MethodSignature methodSignature = (MethodSignature)point.getSignature();
        Method method = methodSignature.getMethod();
        DbSource dbSource = method.getAnnotation(DbSource.class);
        LOGGER.info("select dataSource:" + dbSource.value());
        DataSourceContextHolder.set(dbSource.value());
        try {
            return point.proceed();
        } finally {
            DataSourceContextHolder.remove();
        }
    }
}

SpringBoot实现数据源动态切换的最佳姿势

使用注解切换数据源

最后,在需要的方法上配置相关的数据源注解即可。

@Service
public class UserInfoService {
 
    @Autowired
    private UserInfoMapper userInfoMapper;
 
    @Transactional
    @DbSource(value = "db1")
    public void add(UserInfo entity){
        userInfoMapper.insert(entity);
    }
}

账户服务类,代码示例如下:

@Service
public class AccountInfoService {
 
    @Autowired
    private AccountInfoMapper accountInfoMapper;
 
    @Transactional
    @DbSource(value = "db2")
    public void add(AccountInfo entity){
        accountInfoMapper.insert(entity);
    }
}

采用 aop 代理的方式来切换数据源,业务实现上会更加的灵活。

在上文中,我们介绍了多数据源的配置实现方式,这种配置方式有一个不好的地方在于:配置文件都是写死的。

能不能改成动js态的加载数据源呢,下面我们一起来看看相关的具体实现方式

2.1数据库准备

首先,我们需要准备一张数据源配置表。新建一个test_db数据库,然后在数据库中创建一张数据源配置表,脚本如下:

CREATE TABLE`tb_db_info` (
`id`int(11) unsignedNOTNULL AUTO_INCREMENT,
`db_name`varchar(50) DEFAULTNULL,
`db_url`varchar(200) DEFAULTNULL,
`driver_class_name`varchar(100) DEFAULTNULL,
`username`varchar(80) DEFAULTNULL,
`password`varchar(80) DEFAULTNULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3DEFAULTCHARSET=utf8mb4;

最后,初始化两条数据,方便后续数据源的查询。

2.2修改全局配置文件

我们还是以上文介绍的工程为例,把之前自定义的配置参数删除掉,重新基于 Spring Boot 约定的配置方式,添加相关的数据源参数,内容如下:

# 配置默认数据源
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

2.3编写相关的服务类

基于数据库中tb_db_info表,编写相关的查询逻辑,代码示例如下:

package com.example.dynamic.datasource.entity;
 
publicclass DbInfo {
 
    /**
     * 主键ID
     */
    private Integer id;
 
    /**
     * 数据库pythonkey,即保存Map中的key
     */
    private String dbName;
 
    /**
     * 数据库地址
     */
    private String dbUrl;
 
    /**
     * 数据库驱动
     */
    private String driverClassName;
 
    /**
     * 数据库用户名
     */
    private String username;
 
    /**
     * 数据库密码
     */
    private String password;
 
    // set、get方法等...
}
public interface DbInfoMapper {
 
    List<DbInfo> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dynamic.datasource.mapper.DbInfoMapper">
 
    <select id="findAll" resultType="com.example.dynamic.datasource.entity.DbInfo">
        select
        id
        ,db_name as dbName
        ,db_url as dbUrl
        ,driver_class_name as driverClassName
        ,username
        ,password
        from tb_db_info
        order by id
    </select>
</mapper>

2.4修改动态数据源服务类

DynamicDataSource类进行一些调整,代码如下:

public class DynamicDataSource extends AbstractRoutingDataSource {
 
    @OverrqfGiKZJbide
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.get();
    }
 
    /**
     * 重新加载数据源集合
     * @param dbList
     */
    public void loadDataSources(List<DbInfo> dbList){
        try {
            Map<Object, Object> targetDataSourceMap = new HashMap<>();
            for (DbInfo source : dbList) {
                // 初始化数据源
                DruidDataSource dataSource = new DruidDataSource();
                dataSource.setDriverClassName(source.getDriverClassName());
                dataSource.setUrl(source.getDbUrl());
                dataSource.setUsername(source.getUsername());
                dataSource.setPassword(source.getPassword());
                dataSource.setInitialSize(1);
                dataSource.setMinIdle(1);
                dataSource.setMaxActive(5);
                dataSource.setTestWhileIdle(true);
                dataSource.setTestOnBorrow(true);
                dataSource.setValidationQuery("select 1 ");
                dataSource.init();
                targetDataSourceMap.put(source.getDbName(), dataSource);
            }
            super.setTargetDataSources(targetDataSourceMap);
            // 重新初始化resolvedDataSources对象
            super.afterPropertiesSet();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

2.5修改动态数据源配置类

DataSourceConfig类也需要进行一些调整,通过 Spring Boot 默认的数据源配置类初始化一个数据源实例对象,代码如下:

@Configuration
publicclass DataSourceConfig {
 
    @Autowired
    private DataSourceProperties basicProperties;
 
    /**
     * 注入动态数据源
     * @param dataSource
     * @return
     */
    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource(){
        // 获取初始数据源
        DataSource defaultDataSource = basicProperties.initializeDataSourceBuilder().build();
 
        Map<Object,Object> targetDataSources = new HashMap<>();
        targetDataSources.put("defaultDataSource", defaultDataSource);
        // 注入动态数据源
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
        // 设置动态数据源集
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
}

2.6配置启动时加载数据源服务类

以上的配置调整完成之后,我们还需要配置一个服务启动监听类,将从数据库中查询到的数据配置信息加载到DynamicDataSource对象中,代码示例如下:

SpringBoot实现数据源动态切换的最佳姿势

2.7调整 SpringBootApplication 注解配置

以上的实现方式,因为启动的时候,采用的是 Spring Boot 默认的数据源配置实现,因此无需排除DataSourceAutoConfiguration类,可以将相关参数移除掉。

SpringBoot实现数据源动态切换的最佳姿势

到此这篇关于SpringBoot实现数据源动态切换的最佳姿势的文章就介绍到这了,更多相关SpringBoot数据源动态切换内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新开发

开发排行榜