【问题标题】:Proper way to use multiple transactions in a single class在单个类中使用多个事务的正确方法
【发布时间】:2022-01-18 00:15:21
【问题描述】:

我正在使用 Spring Boot。我可以使用@Transactional 强制对方法进行事务处理。有时我需要某种方法来使用两个或多个事务。

天真的方法行不通:

public void doActions() {
    doAction1();
    doAction2();
}

@Transactional
void doAction1() { ... }

@Transactional
void doAction2() { ... }

因为 Spring 使用代理来实现事务。

通常我使用以下方法:

@Autowired
private ThisService self;

public void doActions() {
    self.doAction1();
    self.doAction2();
}

@Transactional
void doAction1() { ... }

@Transactional
void doAction2() { ... }

它起作用了,但是在 Spring 2.6.0 中,这种循环依赖会导致应用程序无法以可怕的错误启动,除非我将 spring.main.allow-circular-references 设置为 true。

我真的不明白循环引用不好的原因。但显然 Spring Boot 开发人员想要阻止这种设计,所以我想,我最好听从他们的建议。

另一种方法是使用事务管理器并以编程方式调用事务api:

@Autowired
private TransactionTemplate transactionTemplate;

public void doActions() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(@NonNull TransactionStatus status) {
                doAction1();
            }
        });
        transactionTemplate.execute(status -> {
            doAction2();
            return null;
        });

它有效,但在我看来它有点冗长和丑陋。

还有其他我错过的方法吗?我应该将 spring.main.allow-circular-references 设置为 true 吗?我担心开发人员将来会使这些循环引用不受支持,因此我需要重新设计我的应用程序。

【问题讨论】:

  • 在这种情况下,我经常使用一个单独的“执行器”服务,该服务获取一个 lambda 用于执行。调用可能如下所示:executor.executeInNewTx(() -> doAction1()); 等。然后执行程序服务将具有@Transactional 注释等。-本质上,问题是您需要跨越服务边界以进行自动事务处理(即拦截器)才能启动. 另一种选择可能是将您的服务分成多个部分,但拥有这样的执行器服务会使生活更轻松。
  • 您能否详细说明“使用两个或多个事务的某种方法”是什么意思?通常,事务上下文与当前线程相关联,因此每个线程始终只有 一个 活动事务。此外,您的第一种方法的 doActions() 不会创建任何 tx,因为这些是类内调用(参见例如 this question

标签: java spring spring-boot


【解决方案1】:

是的。我同意这很丑。您可以创建一个只负责在事务中执行某些代码的服务。与TransactionTemplate 相同的想法,但它使用@Transational 来管理事务。

@Service
public class TransactionExecutor {

    @Transactional(propagation = Propagation.REQUIRED)
    public <T> T execute(Supplier<T> action) {
        return action.get();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void execute(Runnable action) {
        action.run();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public <T> T executeInNewTx(Supplier<T> action) {
        return action.get();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void executeInNewTx(Runnable action) {
        action.run();
    }

}

然后注入使用它:

@Service
public class FooService {
    
    @Autowired
    private TransactionExecutor txExecutor;


    public void doActions() {
        txExecutor.execute(()->doAction1());
        txExecutor.execute(()->doAction2());
    }

    void doAction1() { ... }

    void doAction2() { ... }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-07
    • 2021-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多