You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确配置Spring R2DBC事务?事务回滚与隔离问题排查

Spring R2DBC+H2事务配置问题及解决方案

问题场景

我在使用Spring R2DBC和H2数据库实现事务时,遇到事务边界配置问题。当前代码手动管理连接和事务,执行流程如下:

  1. 创建连接并开启SERIALIZABLE级别的事务
  2. 执行CREATE TABLE CUSTOMER语句
  3. 延迟60秒
  4. 执行INSERT INTO CUSTOMER语句
  5. 提交事务,异常时回滚

但测试时,在延迟窗口期通过H2控制台执行ALTER TABLE CUSTOMER DROP COLUMN name,后续INSERT操作抛出语法错误,但建表操作并未回滚。需求是实现以下任一效果:

  • 通过隔离策略锁定表,阻止ALTER操作执行
  • 保证事务正常回滚,撤销建表操作

现有代码

主启动类

@SpringBootApplication
@EnableTransactionManagement
public class R2dbcApplication {
    public static void main(String[] args) {
        SpringApplication.run(R2dbcApplication.class, args);
    }
}

Runner组件

@Component
class Runner {
    @Autowired
    ConnectionFactory cf;

    @EventListener(ApplicationStartedEvent.class)
    //@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
    public Mono<Void> runOnStartup(){

        Mono emitter = Mono.from(cf.create())
                .flatMap(c-> Mono.from(c.setTransactionIsolationLevel(IsolationLevel.SERIALIZABLE))
                        .then(Mono.from(c.setAutoCommit(false)))
                        .then(Mono.from(c.beginTransaction()))
                        .then(Mono.from(c.createStatement(
                                        "CREATE TABLE CUSTOMER (id INTEGER, name VARCHAR(255));"
                                ).execute()))
                        .then(Mono.delay(Duration.ofSeconds(60)))
                        .then(Mono.from(c.createStatement(
                                        "INSERT INTO CUSTOMER VALUES ('100','XXX');"
                                ).execute()))
                        .then(Mono.from(c.commitTransaction()))
                        .onErrorResume(ex -> Mono.from(c.rollbackTransaction()).then(Mono.error(ex)))
                        .doFinally((st)->c.close()));

        emitter.subscribe();

        return Mono.empty();
    }
}

配置文件application.yml

spring:
  jdbc:
    url: jdbc:h2:mem:~/db/testdb;DB_CLOSE_DELAY=-1;LOCK_MODE=3
    username: sa
    password:
    initialization-mode: always

  r2dbc:
    url: r2dbc:h2:mem:///~/db/testdb;DB_CLOSE_DELAY=-1;LOCK_MODE=3
    username: sa
    password:
    initialization-mode: always

报错信息

reactor.core.Exceptions$ErrorCallbackNotImplemented: io.r2dbc.spi.R2dbcBadGrammarException: [21002] [21S02] Column count does not match; SQL statement:
INSERT INTO CUSTOMER VALUES ('100','XXX') [21002-214]

解决方案

方案一:实现事务正常回滚

问题根源有两点:

  1. H2默认会自动提交DDL语句,即使开启了事务,导致建表操作提前脱离事务
  2. 手动管理连接和订阅,Spring无法追踪反应式流的错误,回滚逻辑未被正确触发

修正步骤:

  1. 修改H2连接URL:让DDL支持事务,添加MVCC=TRUE和AUTOCOMMIT=FALSE参数
spring:
  r2dbc:
    url: r2dbc:h2:mem:///~/db/testdb;DB_CLOSE_DELAY=-1;LOCK_MODE=3;MVCC=TRUE;AUTOCOMMIT=FALSE
  1. 使用Spring声明式事务+DatabaseClient:替代手动连接管理,让Spring自动处理事务边界
@Component
class Runner {
    private final DatabaseClient databaseClient;

    // 构造注入DatabaseClient
    public Runner(DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }

    @EventListener(ApplicationStartedEvent.class)
    @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
    public Mono<Void> runOnStartup() {
        return databaseClient.sql("CREATE TABLE CUSTOMER (id INTEGER, name VARCHAR(255));")
                .fetch()
                .rowsUpdated()
                .then(Mono.delay(Duration.ofSeconds(60)))
                .then(databaseClient.sql("INSERT INTO CUSTOMER VALUES ('100','XXX');")
                        .fetch()
                        .rowsUpdated())
                .then();
    }
}
  1. 移除手动订阅:由Spring事件监听机制自动处理订阅,确保错误能被捕获并触发回滚

方案二:通过隔离策略锁定表阻止ALTER操作

要阻止其他会话修改表结构,需保证事务持有表的排他锁,结合以下配置:

  1. 保持H2连接URL的LOCK_MODE=3(表级锁)和MVCC=TRUE、AUTOCOMMIT=FALSE配置
  2. 使用SERIALIZABLE隔离级别(已在@Transactional注解中配置)

此时,事务执行CREATE TABLE后会持有表的排他锁,其他会话执行ALTER TABLE时会被阻塞,直到当前事务提交或回滚,从而避免表结构被修改。

关键注意点

  • H2的DDL默认自动提交,必须通过URL配置开启事务支持
  • Spring R2DBC事务管理依赖反应式上下文,尽量使用DatabaseClient和声明式事务,避免手动操作连接
  • 反应式方法内不要手动调用subscribe(),否则Spring无法追踪流的状态变化

内容的提问来源于stack exchange,提问作者user3747396

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 23:48:21