在Spring bean中启动新事务

时间:2010-06-14 12:00:48

标签: java spring transactions

我们有:

@Transactional(propagation = Propagation.REQUIRED)
public class MyClass implementes MyInterface { ...

MyInterface有一个方法:go()

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

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

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


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

@Autowired
MyInterface impl;

impl.go();

3 个答案:

答案 0 :(得分:73)

从Spring参考2.5:

  

使用代理时,@Transactional注释应仅应用于   公众可见度的方法。如果您注释protected,private或   包含@Transactional注释的包可见方法,没有错误   引发,但带注释的方法不会显示已配置的事务   设置。

因此Spring忽略了对非公共方法的@Transactional注释。

此外,

  

在代理模式(默认设置)下,只有“外部”方法调用进入   通过代理将被截获。这意味着'自我调用',   即目标对象内调用目标的其他方法的方法   对象,即使被调用也不会在运行时导致实际的事务   方法标有@Transactional

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

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

有关详细信息,请参阅the reference document

另一种可能的方法是在类本身中获取类的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
    }

}

答案 1 :(得分:42)

由于Spring AOP的工作方式,

@Transactional只会在public方法上被注意到。

但是,如果您愿意,可以TransactionTemplate使用TransactionTemplate txTemplate = new TransactionTemplate(txManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txTemplate.execute(new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) { // do stuff } }); ,例如

{{1}}

答案 2 :(得分: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
   }

} 

firstMethod中的调用调用代理而不是“this”,这将使“REQUIRES_NEW”事务起作用。