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();
}
}