Enable @Transactional on private methods

Problem

在spring中,如果为某个method增加@Transactional注解,则该方法内的数据库操作都处于事务中。

但是,如果方法中有一段业务逻辑在事务之外的话,比如

public void method(){
    // 数据库操作,开启事务
    Result result = handleDbOperation();
    // 业务逻辑处理,不需要事务
    handleBizLogic(result);
}

@Transactional
public void handleDbOperation(){
……
}

则这里的注解其实是不生效的

Why?

Public visibility

spring doc中有相关的说明

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

External call

in proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional

Workaround

Another bean

新建另一个service,将事务相关的代码全部都放在这个service中,从而将内部调用转变为外部调用

@Autowird
private DbService dbservice;

public void method(){
    // 数据库操作,开启事务
    Result result = dbservice.handleDbOperation();
    // 业务逻辑处理,不需要事务
    handleBizLogic(result);
}

TransactionUtil

同样是转为外部调用,但是比较巧妙的是,这里利用了lambda表达式作为传参,从而不需要将代码逻辑进行迁移。

@Autowird
private TransactionHelper helper;

public void method(){
    // 数据库操作,开启事务
    Result result = helper.withTransaction(() -> handleDbOperation());
    // 业务逻辑处理,不需要事务
    handleBizLogic(result);
}

// TransactionHelper.java :
@Service
public class TransactionHelper {
    @Transactional
    public <T> T withTransaction(Supplier<T> supplier) {
        return supplier.get();
    }
    @Transactional
    public void withTransaction(Runnable runnable) {
        runnable.run();
    }
}

发表评论