前言
最近项目需求,需要用到多数据源配置,经过一番学习完成需求功能开发,本文介绍一下我用到的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注解,可以在代码块中手动开启提交回滚事务,也可以达到效果。