SpringBoot多数据源@DS和@Transactional冲突

背景介绍

因为最近项目中使用 @DS 注解实现多数据源切换时,在一个controller中同时操作多个数据源的service方法,为了保证这一组操作下数据的完整性,则需要使用事务@Transaction进行保证,于是直接在controller方法上加上了该注解,以为没有问题,结果在切换数据源的时候报错找不到另一个数据源的数据表

问题解决

分析解决

现在我们先来看一下,是怎么解决问题的。
首先,在主方法上加 @Transactional(rollbackFor = Exception.class),默认事务的传播机制是,PROPAGATION_REQUIRED ,可以不指定。
然后,在切换数据源的方法类上,添加 @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) ,指定事务的传播机制是,Propagation.REQUIRES_NEW。
至此,冲突解决!

直接上代码

    @Transactional(rollbackFor = Exception.class)
    @DS("db1")
    @Override
    public Result 主方法() {
    }
     @DS("db2")
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public void 切换数据源的方法(HtyyOrder htyyOrder) {
    }

原因解析

解决办法就是将原本的一个事务拆分成两个事务。你可以试一下,只在主方法中加事务,你会发现在切面里看数据源切换了,但事务内的数据源依然是旧的,这样就会报出XXX表找不到的问题。那么到底是什么原因导致一个事务内的数据源没有随着@DS(“db2”)注解做出动态数据源切换呢?

  1. .因为SPRING在开启事务的同时,会去数据库连接池拿数据库连接。如果仅在主方法上添加@TRANSACTIONAL,那么,TRANSACTIONINTERCEPTOR 会使用 SPRING DATASOURCETRANSACTIONMANAGER 创建事务,并将事务信息(获取数据源CONNECTION连接,此时获取到的数据源是默认配置的BASE数据源信息)连接信息,通过 THREADLOCAL 绑定在当前线程。
  2. 此时当前线程事务绑定的连接信息是BASE数据源,当我们在内层切换数据源方法上使用@DS切换数据源,并没有重新开启新事务,没有改变当前线程事务的连接信息,仅仅是做了一次拦截,改变了DATASOURCEHOLDER的栈顶DATASOURCE,对于整个事务的连接是没有影响的,所以会产生数据源没有切换的问题。
  3. 所以我这里的解决办法是,将保证住方法操作数据完整性的事务,拆解成两个事务,在切换数据源方法类上,除了添加切换数据源的注解@DS(“DB2”),再添加@TRANSACTIONAL(PROPAGATION = PROPAGATION.REQUIRES_NEW, ROLLBACKFOR = EXCEPTION.CLASS),新建开一个事务,获取新数据源CONNECTION连接。
<think>好的,我现在需要解决Spring Boot项目中多数据源配置不生效的问题。首先,我要回忆一下用户提供的引用内容。引用[1]提到Druid自定义数据源不生效可能是因为bean没有正确注入,导致spring.datasource没有使用自定义的数据源。引用[2]则讨论了使用dynamic-datasource-spring-boot-starter配置多数据源时出现url找不到的问题,并给出了依赖配置的例子。 用户的问题是多数据源配置不生效,可能涉及多个方面。我需要分步骤来分析可能的原因解决方案。 首先,检查依赖是否正确。根据引用[2],dynamic多数据源需要添加对应的依赖。如果依赖没有正确引入,配置自然不会生效。比如是否遗漏了dynamic-datasource-spring-boot-starter的依赖,或者在Maven中没有正确更新。 其次,配置文件的设置。多数据源通常需要在application.yml或application.properties中配置各个数据源的url、username、password等信息。比如主数据源从数据源的配置是否正确,是否有拼写错误,特别是数据源的名称是否符合规范。例如,dynamic-datasource要求主数据源使用特定的标识,如primary,其他数据源则自定义名称,但需要正确指定。 然后,数据源Bean的注入问题。根据引用[1],如果自定义数据源没有生效,可能是自动配置类覆盖了自定义的Bean。比如,Spring Boot的自动配置可能会优先使用默认的数据源配置,而忽略用户自定义的。这时候可能需要排除自动配置类,比如使用@SpringBootApplication的exclude属性排除DataSourceAutoConfiguration,或者在配置文件中设置spring.autoconfigure.exclude。 接下来,检查注解的使用是否正确。在使用dynamic-datasource时,通常需要在Service层的方法上使用@DS注解来指定数据源。如果忘记添加注解,或者注解的值与配置中的数据源名称不匹配,也会导致数据源切换不生效。需要确认@DS("slave")中的"slave"是否配置文件里的从数据源名称一致。 还有可能存在组件扫描的问题。如果数据源配置类所在的包没有被Spring Boot的组件扫描覆盖,那么相关的Bean不会被创建。需要确保配置类有@Configuration注解,并且位于主应用类所在的包或其子包下。 此外,动态数据源的配置是否正确初始化。比如,是否在配置类中正确创建了动态数据源,并将各个具体数据源添加到动态数据源的路由中。有时候需要手动配置动态数据源并设置默认数据源。 还有可能的问题包括数据源连接参数错误,比如数据库URL格式不正确,驱动类未正确指定,或者数据库服务不可用。例如,MySQL的URL格式是jdbc:mysql://host:port/dbname,如果端口或主机名写错,会导致连接失败。 最后,查看日志信息。Spring Boot启动时的日志会显示数据源初始化的相关信息,如果数据源没有正确配置,通常会有错误提示。比如无法创建数据源Bean,或者找不到URL属性等。根据日志中的错误信息,可以更精准地定位问题。 总结下来,可能的解决步骤包括:检查依赖、排除自动配置、核对配置项、确认注解使用、验证组件扫描、检查连接参数、查看启动日志等。需要逐一排查这些可能性,找到导致多数据源配置不生效的具体原因,并进行相应的修正。</think>### Spring Boot 多数据源配置不生效的解决方案 #### 1. 检查依赖配置 确保项目中已正确引入多数据源依赖。例如,使用`dynamic-datasource-spring-boot-starter`时需添加以下依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.1</version> </dependency> ``` 若未正确引入依赖,配置将无法加载[^2]。 #### 2. 排除自动配置类冲突 Spring Boot默认会优先加载单数据源的自动配置(`DataSourceAutoConfiguration`),可能导致多数据源配置被覆盖。需在主启动类中排除自动配置: ```java @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 或在`application.yml`中配置: ```yaml spring: autoconfigure: exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure ``` #### 3. 核对数据源配置项 在`application.yml`中,确保多数据源配置格式正确。示例: ```yaml spring: datasource: dynamic: primary: master # 设置默认数据源 datasource: master: url: jdbc:mysql://localhost:3306/db1 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/db2 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` 注意: - 配置层级需符合`dynamic-datasource`的规范,如`spring.datasource.dynamic.datasource`。 - 数据源名称(如`master`/`slave`)需与代码中`@DS("slave")`的注解值匹配。 #### 4. 验证注解使用 在需要切换数据源的方法或类上添加`@DS`注解,并确保名称与配置一致: ```java @Service public class UserService { @DS("slave") // 指定从数据源 public List<User> getUsers() { return userMapper.selectList(); } } ``` 若注解未生效,可能是AOP代理未生效,需检查是否开启事务管理或切面配置。 #### 5. 组件扫描范围 确保多数据源配置类(如`DataSourceConfig`)位于Spring Boot主类同级或子包下。若配置类未被扫描到,会导致Bean未注入。 #### 6. 检查连接参数 - **URL格式**:MySQL的URL需包含时区(如`serverTimezone=UTC`),例如: `jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=UTC`。 - **驱动类**:`driver-class-name`需与数据库版本匹配,MySQL 8+使用`com.mysql.cj.jdbc.Driver`。 #### 7. 查看启动日志 启动时观察日志中是否输出多数据源初始化信息。若出现以下错误,需针对性解决: - `Failed to configure a DataSource`:数据源配置缺失或格式错误。 - `No qualifying bean of type DataSource`:Bean未成功注入,检查依赖排除项。 #### 8. 其他常见问题 - **Druid连接池配置冲突**:若同时引入Druid依赖,需排除其自动配置: ```yaml spring: autoconfigure: exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure ``` - **多数据源事务管理**:需使用`@Transactional`时,建议配合`JTA`或`Atomikos`实现分布式事务。 --- ### 示例修正后的多数据源配置 **application.yml** ```yaml spring: datasource: dynamic: primary: master strict: false # 是否严格匹配数据源,默认false datasource: master: url: jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/slave_db?useSSL=false&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` **Service层代码** ```java @Service @DS("master") // 类级别默认数据源 public class OrderService { @DS("slave") // 方法级别切换数据源 public List<Order> getOrders() { return orderMapper.selectList(); } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值