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 中的实现方式。
1.1数据库准备
创建两个库,分别是db_test_1
和db_test_2
。db_test_1
数据库中创建一张用户表,脚本如下:
db_test_2
数据库中创建另一张账户表,脚本如下:
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编写多数据源
创建动态数据源服务类
首先,创建一个DynamicDataSource
类,并继承AbstractRoutingDataSource
抽象类,同时重写determineCurrentLookupKey()
方法,代码示例如下:
创建动态数据源缓存类
创建一个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
文件中添加相关的数据源变量,内容如下:
排除自动装配数据源
需要在注解@SpringBootApplication
类上排除自动装配数据源配置,内容如下:
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(); } } }
使用注解切换数据源
最后,在需要的方法上配置相关的数据源注解即可。
@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
对象中,代码示例如下:
2.7调整 SpringBootApplication 注解配置
以上的实现方式,因为启动的时候,采用的是 Spring Boot 默认的数据源配置实现,因此无需排除DataSourceAutoConfiguration
类,可以将相关参数移除掉。
到此这篇关于SpringBoot实现数据源动态切换的最佳姿势的文章就介绍到这了,更多相关SpringBoot数据源动态切换内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论