先描述一下问题,别没用还往下看耽误大家时间:

问题现象就是调用的方法加了GlobalTransactional注解,但是出现异常之后,期望seata帮我回滚数据库,实际上却没有回滚。并且seata的服务器回显了日志回滚成功,但是我去数据库查发现两个表的数据都刷新成功,并没有回显。日志如下所示:

00:53:22.174  INFO --- [verHandlerThread_1_20_500] io.seata.server.coordinator.DefaultCore 
 : Rollback global transaction successfully, xid = ip:8091:1745452184813340216.

问题根因

我这边前面准备工作很充分,undo_log是有这个表的,经过deepseek对现象的一通分析发现是seata没有正确代理我的数据源DataSource。(很明显的现象就是undo_log在这个过程中一直没有任何数据)

问题解决

解决就是能让seata代理数据源。有两种方式,一种是yml增加配置

seata: 
	enable-auto-data-source-proxy: true

这个我加了之后没效果,一脸懵,去日志查询dataSource还是没有被代理,然后就用方法二,手动加代理,DataSourceConfig,如下所示代码:(**用这个先关上面的哈,方法一改为false)**这里我因为启动时候看到是HikariDataSource ,所以专门加的这个类型的。

@Configuration
public class DataSourceConfig {

    /**
     * 1. 创建一个 HikariDataSource 实例
     * 2. 使用 @ConfigurationProperties 将 application.yml 中所有 'spring.datasource' 开头的配置
     * (如 url, username, password, hikari.* 等)自动绑定到这个数据源上。
     * 3. 不需要在代码中手动设置任何属性!
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource") // 这是关键
    public HikariDataSource hikariDataSource() {
        return new HikariDataSource();
    }

    /**
     * 4. 将 HikariDataSource 包装成 Seata 的 DataSourceProxy
     * 5. 使用 @Primary 注解确保当有多个 DataSource Bean 时,优先注入这个
     */
    @Bean
    @Primary // 这个注解很重要
    public DataSource dataSource(HikariDataSource hikariDataSource) { // 注入上面定义的Bean
        return new DataSourceProxy(hikariDataSource);
    }
}

这时候问题又来了,用这个手动增加代理的方式我这边启动报错,日志如下:

[/DataSourceConfig.class]: Bean instantiation via factory method failed;
 nested exception is org.springframework.beans.BeanInstantiationException: Failed to 
 instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested
  exception is java.lang.IllegalArgumentException: jdbcUrl is required with 
  driverClassName.

这个报错就很明显,意思是jdbcUrl查不到,,可是我前面明明通过@ConfigurationProperties(prefix = “spring.datasource”) 引入yml的配置了,为什么还没有?
再看看我的yml配置就清楚了。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false #
    username: root 
    password: root 
    driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8.x 的驱动类:cite[6]

这个配置很正常,但是发现是url而不是jdbcUrl,查询之后发现HikariDataSource 连接池的url正确配置是jdbc-url

spring:
  datasource:
    jdbc-url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false #
    username: root 
    password: root 
    driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8.x 的驱动类:cite[6]

这样修改之后就可以启动,然后现象就符合预期了。问题解决。
日志也显示被seata代理了:

2025-08-29 01:32:25.992  INFO 15124 --- [           main] .s.s.a.d.SeataAutoDataSourceProxyCreator : Auto proxy of [hikariDataSource]
2025-08-29 01:32:26.022 ERROR 15124 --- [           main] com.zaxxer.hikari.HikariConfig           : HikariPool-1 - dataSource or dataSourceClassName or jdbcUrl is required.

其他知识(方法二)

spring-boot-starter-jdbc 本身并不直接包含任何一个连接池的实现(如 HikariCP、DBCP2、Druid)。它只是一个“启动器”,它的作用是:引入一系列与 JDBC 操作相关的依赖(如 spring-jdbc, spring-tx)。

关键点在于:当 Spring Boot 尝试自动配置 DataSource 时,它会在项目的类路径(Classpath)上扫描,按优先级自动选择一个它发现的连接池实现。其默认优先级为:HikariCP > Tomcat JDBC Pool > Commons DBCP2。

其实这里也可以在 pom.xml 中显式添加一个连接池的依赖。添加后,不用HikariCP连接池,然后在yml文件增加seata: enable-auto-data-source-proxy: true 就可以删除DataSourceConfig文件了。(对我这种强迫症,能用配置就配置的人来说比较友好。。)

1、框架介绍(未完待定。。。)

今天搭建了一个springcloudaAlibaba+nacos+openFeign+seatad的项目,想验证一下是否能正确对分布式事务进行回滚。搭建时候数据库相关的只引入了这两个依赖(埋坑的开始。。。)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.22</version> <!-- 与你的MySQL服务器版本一致 -->
    <scope>runtime</scope>
</dependency>

项目本身很简单,一个Order服务,一个Stock服务,订单服务新增时候会调用库存服务的减库存接口,代码大概这样。

@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    private StorageService storageService;

    @Resource
    private OrderServiceImpl orderService;

    @RequestMapping("/add/{id}")
    @GlobalTransactional
    public String add(@PathVariable int id) {
        OrderDao orderDao = new OrderDao();
        int productId = 1;
        int nums = 10;
        orderDao.setId(id);
        orderDao.setProductId(productId);
        orderDao.setTotalAmount(nums);
        Integer integer = orderService.insertOrder(orderDao);
        System.out.println(integer);
        String change = storageService.change(orderDao.getTotalAmount(),orderDao.getProductId());
        System.out.println(1/0);
        return "success" + change;
    }
}
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐