Why are transactions rolled back even when propagating = Propagation.REQUIRES_NEW in the second method in the Spring class?

Now all the basic settings are now fine, and I started using transactions. Struts + Spring + Hibernate Annotation Transaction Manager. This is an example code in Action that will call a class of service:

userService.addUser(); 

Here is the addUser() method in the service class:

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

First, I called addUser in userDao, which will insert the user. Secondly, I called addUser2 another method in this service class.

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

And this will not succeed due to zero PK. I believe that the second call ( addUser2 ) will not work, but will not affect the previous one. However, the user is not inserted.

If I just call:

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

It works, that is, basic settings, such as a database, are not incorrect.

Any idea?

+4
source share
4 answers

Spring declarative transaction processing using AOP proxies. When you get a tranasactional bean, you actually get a proxy server that wraps your bean instance, intercepts a method call, starts a transaction if necessary, then calls the actual bean method, and then commits or rolls back the transaction, if necessary.

But you are calling your bean method from another method inside the same bean, so the proxy bypasses and cannot apply transactional behavior.

Put this method in another bean or use AspectJ, which controls byte code and can intercept method calls inside the bean.

A more detailed explanation can be found in the Spring documentation.

+6
source

I did some tests and found a conclusion.

  • If the 2nd service (internal) is REQUIRED and throws an exception, even the first transaction will catch it, both of them will be rolled back (because they are on the same boat!)

  • If the 2nd service (internal) REQUIRES_NEW and throws an Exception, the external should deal with this rollback (this is NOT my rollback, but I have to do something), if I do not catch it, this exception will trigger an external trigger for rollback ( even this is not his exception, but this is the exception!). Thus, the external must do something for this situation (set a rollback or catch it).

Is it correct?

+1
source

This is because of the Spring AOP architecture.

Spring AOP uses a proxy, so aspects are simply executed when methods are called in the proxy.

When you call this.addUser2(...) you call the method on the self object, not the proxy. Thus, no aspect is performed, and TX control is not performed.

You can do the following things:

  • Move the addUser2 method to another bean (for example, UserService2 ), and then enter the new bean in UserService and call this method with userService2.addUser2() .
  • Inject UserService in UserService (I'm not sure if this can be done) and call addUser2() using userService.addUser2() not this.addUser2() .
0
source

Just as Amir Pashazade said that he did not know what to call a proxy with a transactional context in the same bean, here is an example:

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

Beware that Spring seems to throw an UnexpectedRollbackException if you selected an exception in addUser2, but the transaction has already been marked for rollback.

0
source

All Articles