2

我正在使用带有 webflux 的 spring-boot 2.4.2 来连接到 postgres 数据库。我在使用时观察到一种@Transactional我不理解的行为。

为了展示这种行为,我创建了一个示例应用程序,它尝试将行添加到两个表中;表“a”和表“b”。对表“a”的插入预计会因重复键违规而失败。鉴于使用了事务性,我希望不会将任何行添加到表“b”中。

但是,根据我注释的方法,@Transactional我会得到不同的结果。

如果我注释控制器方法,事情会按预期工作,并且不会向表 B 添加任何行。

    @PostMapping("/")
    @Transactional
    public Mono<Void> postEntities() {
        return demoService.doSomething();
    }

DemoService 看起来像这样:

    public Mono<Void> doSomething() {
        return internal();
    }


    public Mono<Void> internal() {
        Mono<EntityA> clash = Mono.just(EntityA.builder().name("clash").build()).flatMap(repositoryA::save);
        Mono<EntityB> ok = Mono.just(EntityB.builder().name("ok").build()).flatMap(repositoryB::save);
        return ok.and(clash);
    }

如果我将@Transactional注释从控制器移动到doSomething(),那么事务仍然按预期工作。但是,如果我将@Transactional注释移至internal(),则事务不会按预期工作。一行被添加到表“b”。

这个例子的完整代码在这里:https ://github.com/alampada/pg-spring-r2dbc-transactional

我不明白为什么将注释移动到internal()方法会导致事务处理出现问题。你能解释一下吗?

4

1 回答 1

3

来自 Spring 参考文档:使用 @Transactional

在代理模式下(默认),只有通过代理传入的外部方法调用会被拦截。这意味着自调用(实际上,目标对象中的一个方法调用目标对象的另一个方法)在运行时不会导致实际的事务,即使调用的方法标记有@Transactional。此外,代理必须完全初始化以提供预期的行为,因此您不应在初始化代码(即@PostConstruct)中依赖此功能。

这里从 doSomething() 到 internal() 的调用是自调用。

请注意 Spring Framework 的声明式事务支持是通过 AOP 代理启用的。

Spring 参考文档:了解 AOP 代理将清楚说明为什么自调用在代理上不起作用。请通读从这里开始的部分, 这里要理解的关键是 main(..) 中的客户端代码

于 2021-01-21T12:21:22.710 回答