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