在Spring bean中启动新事务

Mar*_*eon 44 java spring transactions

我们有:

@Transactional(propagation = Propagation.REQUIRED)
public class MyClass implementes MyInterface { ...
Run Code Online (Sandbox Code Playgroud)

MyInterface有一个方法:go().

当go()执行时,我们启动一个新的事务,该事务在方法完成时提交/回滚 - 这很好.

现在让我们说在go()中我们调用MyClass中的私有方法@Transactional(propagation = Propagation.REQUIRES_NEW.似乎Spring"忽略"REQUIRES_NEW注释并且不会启动新事务.我相信这是因为Spring AOP在接口级别(MyInterface)上运行,并且不拦截对MyClass方法的任何调用.它是否正确?

有没有办法在go()事务中启动新事务?是否只能调用另一个配置为REQUIRES_NEW的事务的Spring托管bean?


更新:在客户端执行时添加,go()通过对接口的引用而不是类来执行:

@Autowired
MyInterface impl;

impl.go();
Run Code Online (Sandbox Code Playgroud)

Abh*_*kar 77

从Spring参考2.5:

使用代理时,@Transactional注释应仅应用于具有公共可见性的方法.如果使用注释对带有保护的,私有的或包可见的方法进行@Transactional注释,则不会引发任何错误,但带注释的方法将不会显示已配置的事务设置.

所以Spring忽略@Transactional了非公共方法的注释.

也,

在代理模式(默认设置)下,只会拦截通过代理进入的"外部"方法调用.这意味着'自调用',即目标对象中调用目标对象的其他方法的方法,即使被调用的方法被标记,也不会在运行时导致实际的事务@Transactional!

因此,即使您创建了方法public,在同一个类的方法中调用它也不会启动新事务.

您可以aspectj在事务设置中使用模式,以便在类中编译与事务相关的代码,并且不会在运行时创建代理.

有关详细信息,请参阅参考文档.

另一种可能的方法是在类本身中获取类的spring代理并在其上调用方法而不是this:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class SomeService {

    @Autowired
    private ApplicationContext applicationContext;

    private SomeService  getSpringProxy() {
        return applicationContext.getBean(this.getClass());
    }

    private void doSomeAndThenMore() {
        // instead of
        // this.doSometingPublicly();
        // do the following to run in transaction
        getSpringProxy().doSometingPublicly();
    }

    public void doSometingPublicly() {
        //do some transactional stuff here
    }

}
Run Code Online (Sandbox Code Playgroud)


ska*_*man 42

@Transactional只有当它在一个public方法上时才会被注意到,因为Spring AOP的工作方式.

但是,如果您愿意,可以通过编程方式启动新事务TransactionTemplate,例如

TransactionTemplate txTemplate = new TransactionTemplate(txManager);                
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallback<Object>() {
    public Object doInTransaction(TransactionStatus status) {
        // do stuff
    }
});
Run Code Online (Sandbox Code Playgroud)

  • @Marcus:排序.如果`MyClass`实现了一个接口,那么Spring将只使用该接口来生成事务代理,甚至忽略公共的非接口方法.但是,如果`MyClass`没有实现接口,那么将使用所有公共方法. (4认同)

wml*_*ski 5

简而言之,您必须通过代理调用方法来实现事务行为.可以在问题中询问的同一个bean中调用"REQUIRES_NEW".要做到这一点,你必须做一个"自我"的参考.在春天,它不是直截了当的.你必须用@Resource注释注入它.

@Service("someService")
public class ServieImpl implements Service {

   @Resource(name = "someService")
   Service selfReference;

   @Transactional
   public void firstMethod() {
       selfReference.secondMethod();
   }

   @Transactional(propagation = Propagation.REQUIRES_NEW) 
   public void secondMethod() {    
         //do in new transaction
   }

} 
Run Code Online (Sandbox Code Playgroud)

firstMethod中的调用调用代理而不是"this",它应该使"REQUIRES_NEW"事务起作用.