为什么即使在Spring服务类的第二种方法中传播= Propagation.REQUIRES_NEW时,事务也会回滚?

基本设置现在都很好,我开始尝试交易。 Struts + Spring + Hibernate注释事务管理器。 这是Action中的示例代码,将调用服务类:

userService.addUser(); 

这是服务类中的addUser()方法:

  @Transactional(value="deu" ) public void addUser() { userDao.addUser(); this.addUser2(); } 

首先,我在userDao中调用了addUser,它将插入一个用户。 其次,我在此服务类的另一个方法中调用了addUser2

  @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW ) public void addUser2() { //should be a new transaction and will not affect the previous one. //this one will fail but should not affect the previous one. userDao.addUserFail(); } 

由于无效PK,这一次将失败。 我想第二次调用( addUser2 )会失败,但不会影响前一次调用。 但是,未插入用户。

如果我只打电话:

  @Transactional(value="deu" ) public void addUser() { userDao.addUser(); //this.addUser2(); } 

它正在工作,这意味着像数据库这样的基本设置没有错。

任何想法?

Spring的声明式事务处理使用AOP代理。 当你得到一个tranasactional bean时,你实际上得到一个包装你的bean实例的代理,拦截方法调用,必要时启动一个事务,然后调用实际bean的方法,然后在必要时提交或回滚事务。

但是你从同一个bean中的另一个方法调用bean的方法,因此代理被绕过,并且不能应用任何事务行为。

将该方法放在另一个bean中,或者使用AspectJ,它可以检测字节代码并拦截bean内方法调用。

有关更详细的说明,请参阅Spring文档 。

我做了一些测试并找到了结论。

  1. 如果第二个服务(内部)是必需的并抛出exception,即使是第一个事务捕获它,它们都会回滚(因为它们在同一条船上!)

  2. 如果第二个服务(内部)是REQUIRES_NEW并抛出exception,外部必须处理这个回滚(这不是我的回滚,但我必须做一些事情),如果没有捕获它,外部的这个exception将触发外部回滚(甚至这不是他的例外,但它是一个例外!)。 所以外部必须为这种情况做一些事情(设置回滚或捕获它)。

这样对吗 ?

这是因为Spring AOP架构。

Spring AOP使用代理,因此在代理上调用方法时只执行方面。

调用this.addUser2(...)您调用自身对象上的方法,而不是代理。 因此,不执行任何方面,也不执行TX管理。

你可以做以下事情:

  • addUser2方法移动到另一个bean(例如UserService2 ),然后将新bean注入UserService并使用userService2.addUser2()调用该方法。
  • UserService注入UserService (我不确定是否可以完成),并使用userService.addUser2()而不是this.addUser2()调用addUser2() this.addUser2()

就像Amir Pashazadeh说他不知道如何在同一个bean中使用事务上下文调用代理一样,这是一个例子:

 @Component public class UserService(){ @Autowired @Setter private ApplicationContext applicationContext; @Autowired @Setter private UserDao userDao; @Transactional(value="deu" ) public void addUser() { userDao.addUser(); try{ getProxy().addUser2(); catch(Exception ex){ // Avoid rolling back main transaction log("OMG it failed!!") } } @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW ) public void addUser2() { //should be a new transaction and will not affect the previous one. //this one will fail but should not affect the previous one. userDao.addUserFail(); } private UserService getProxy() { return applicationContext.getBean(UserService.class); } } 

请注意,如果您在addUser2中捕获了exception但是事务已标记为Rollback,则Spring似乎抛出UnexpectedRollbackException 。