在单个类中使用多个事务的正确方法

vbe*_*nar 4 java spring spring-boot

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

天真的方法是行不通的:

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

@Transactional
void doAction1() { ... }

@Transactional
void doAction2() { ... }
Run Code Online (Sandbox Code Playgroud)

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

通常我使用以下方法:

@Autowired
private ThisService self;

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

@Transactional
void doAction1() { ... }

@Transactional
void doAction2() { ... }
Run Code Online (Sandbox Code Playgroud)

它有效,但在 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;
        });
Run Code Online (Sandbox Code Playgroud)

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

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

Ken*_*han 8

是的。我同意这很丑。您可以创建一个只负责执行事务中的某些代码的服务。与此相同的想法,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();
    }

}
Run Code Online (Sandbox Code Playgroud)

然后注入它来使用它:

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


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

    void doAction1() { ... }

    void doAction2() { ... }

}
Run Code Online (Sandbox Code Playgroud)