springboot使用dynamic做多数据源配置

前言

最近项目需求,需要用到多数据源配置,经过一番学习完成需求功能开发,本文介绍一下我用到的dynamic做动态数据源配置。

1. 依赖导入

这里用到dynamic依赖,和对应的数据源(mysql,oracle等)依赖。以下是dynamic依赖:

	<dependency>
		<groupId>com.baomidou</groupId>
		<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
		<version>3.5.0</version>
	</dependency>

2.yml配置

yml配置主要是将多个数据源分别配置,并指定默认数据源,具体yml配置参考如下:

spring:
  datasource:
    dynamic:
      primary: kingbase # 设置主库为kingbase
      strict: false # 严格匹配数据源,默认false,当为true时,未匹配到指定数据源时抛异常,false未匹配到指定数据源会使用默认数据源
      datasource:
        kingbase: # 这的kingbase是自定义的数据源名称,不是指数据库的类型
          url: jdbc:kingbase8://${DATABASE_HOST:127.0.0.1}:${DATABASE_PORT:54323}/${DATABASE_DBNAME:kingbase}?serverTimezone=GTM%2B8
          username: ${DATABASE_USERNAME:SYSTEM}
          password: ${DATABASE_PASSWORD:12345}
          driverClassName: com.kingbase8.Driver
          type: com.alibaba.druid.pool.DruidDataSource
        mysql:
          url: jdbc:mysql://${DATABASE_HOST:127.0.0.1}:${DATABASE_PORT:3306}/${DATABASE_DBNAME:mysql}?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
          username: ${DATABASE_USERNAME:root}
          password: ${DATABASE_PASSWORD:1234}
          driverClassName: com.mysql.cj.jdbc.Driver
          type: com.alibaba.druid.pool.DruidDataSource

使用时,只需要在对应的mapper上,使用注解@DS(“mysql”),指定当前mapper数据源为mysql数据源

3.多数据源失效问题

常见的多数据源失效问题可以从配置方面去排查,例如,yml配置,DS指定数据源配置等方面。
这里我介绍一下我在使用过程中遇到的多数据源失效问题。

问题情境

我在同一个方法中,先调用了主数据源(kingbase)中的某个表数据,然后再调用了从数据源(mysql)中的数据,然后执行下面业务逻辑,我这后面涉及到对数据库的写操作,我在方法上用了@Transactional注解管理事务,然后我发现,调用主数据源的时候,数据获取正常,调用从数据源的时候,报错没有xx表,查看报错信息为kingbaseSQLException,就知道了调用的主数据源数据,多数据源失效了。于是开始排查

问题排查

(1) 配置问题
首先查看是否是配置原因,检查yml配置和mapper配置是否正确,然后我写了个测试接口,测试多数据源是否有效,测试接口直接调用获取从数据源数据,获取正确,不是这个原因。
(2)方法代理问题
我将获取数据的代码单独封装方法,然后通过自动注入service,用注入的service调用方法获取对应所需的数据,仍然发现此问题,不是这个原因。
(3)事务管理问题
这里我查询了相关资料,得知在方法上添加事务,事务只会对一个数据源进行事务管理。其实说得通俗易懂一点就是,一个数据库的事务管不住另一个数据库的事务。
ok,问题找到现在开始解决问题。

问题解决

根据我这问题产生原因,思路很简单,对每个数据源数据获取单独使用事务管理,这里可以了解一下spring的事务传播机制,默认传播机制为Propagation.REQUIRED(如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中),所以在我前面使用方法代理排查问题的时候,还是不行,因为使用的默认传播机制。
这里补充一下事务传播机制:

PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。- 默认
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

基于上面提到的事务传播机制,我这将对数据库的操作封装成独立方法,然后我使用PROPAGATION_REQUIRES_NEW传播机制,新建事务,使用方式@Transactional(propagation = Propagation.REQUIRES_NEW)。然后再次测试功能,发现报另一个错了,这就说明这个多数据源失效问题,已经解决。我这的另一个错产生原因主要是项目中使用到了Neo4j,而这个事务只支持默认事务,所以报错。

问题解决其他思路

我这因为Neo4j不适合使用多个事务进行事务管理。所以我这将代码逻辑进行了处理,我将接口的入口方法,单独封装方法,然后将数据进行业务逻辑处理后,统一添加到数据库和Neo4j中,这里我将数据添加逻辑单独封装方法进行处理,最后问题解决,功能测试通过。


其实也可以在代码中不使用@Transcational注解,可以在代码块中手动开启提交回滚事务,也可以达到效果。

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个比较具体的技术问题。首先,Spring Boot可以通过配置多个数据源来实现多数据源的切换。其次,可以使用AOP技术,在方法执行前动态切换数据源。以下是一些关键的代码片段: 1. 在application.properties文件中配置多个数据源 ``` spring.datasource.primary.url=jdbc:mysql://localhost:3306/db1 spring.datasource.primary.username=user1 spring.datasource.primary.password=pass1 spring.datasource.secondary.url=jdbc:mysql://localhost:3306/db2 spring.datasource.secondary.username=user2 spring.datasource.secondary.password=pass2 ``` 2. 创建多个数据源的Bean ``` @Configuration public class DataSourceConfig { @Bean(name = "primaryDataSource") @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "secondaryDataSource") @ConfigurationProperties(prefix = "spring.datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } } ``` 3. 创建动态数据源并设置默认数据源 ``` @Configuration public class DynamicDataSourceConfig { @Autowired @Qualifier("primaryDataSource") private DataSource primaryDataSource; @Autowired @Qualifier("secondaryDataSource") private DataSource secondaryDataSource; @Bean(name = "dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.PRIMARY.name(), primaryDataSource); targetDataSources.put(DataSourceType.SECONDARY.name(), secondaryDataSource); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(primaryDataSource); return dynamicDataSource; } @Bean public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dynamicDataSource()); } } ``` 4. 创建AOP切面,在方法执行前根据注解切换数据源 ``` @Aspect @Component public class DataSourceAspect { @Autowired @Qualifier("dynamicDataSource") private DataSource dynamicDataSource; @Pointcut("@annotation(com.example.demo.datasource.DataSource)") public void dataSourcePointCut() {} @Before("dataSourcePointCut()") public void before(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); DataSource dataSource = signature.getMethod().getAnnotation(DataSource.class); if (dataSource != null) { DataSourceType dataSourceType = dataSource.value(); DynamicDataSourceContextHolder.setDataSourceType(dataSourceType.name()); } } @After("dataSourcePointCut()") public void after(JoinPoint joinPoint) { DynamicDataSourceContextHolder.clearDataSourceType(); } } ``` 5. 在需要切换数据源的方法上添加注解 ``` @DataSource(DataSourceType.SECONDARY) public List<User> listUsers() { return jdbcTemplate.query("SELECT * FROM user", new BeanPropertyRowMapper<>(User.class)); } ``` 这样就可以实现动态切换多数据源了。希望能够帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值