Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Starting new transaction in Spring bean

We have:

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

MyInterface has a single method: go().

When go() executes we start a new transaction which commits/rollbacks when the method is complete - this is fine.

Now let's say in go() we call a private method in MyClass that has @Transactional(propagation = Propagation.REQUIRES_NEW. It seems that Spring "ignores" the REQUIRES_NEW annotation and does not start a new transaction. I believe this is because Spring AOP operates on the interface level (MyInterface) and does not intercept any calls to MyClass methods. Is this correct?

Is there any way to start a new transaction within the go() transaction? Is the only way to call another Spring managed bean that has transactions configured as REQUIRES_NEW?


Update: Adding that when clients execute go() they do so via a reference to the interface, not the class:

@Autowired MyInterface impl;  impl.go(); 
like image 566
Marcus Leon Avatar asked Jun 14 '10 12:06

Marcus Leon


People also ask

How do I start a Spring transaction?

You can use aspectj mode in transaction settings so that the transaction related code is weaved in the class and no proxy is created at runtime. See the reference document for more details. Show activity on this post. @Transactional will only be noticed if it's on a public method, due to the way Spring AOP works.

How do I enable transactions in spring boot?

However, if we're using a Spring Boot project and have a spring-data-* or spring-tx dependencies on the classpath, then transaction management will be enabled by default.

What is use of @transactional annotation in spring?

So when you annotate a method with @Transactional , Spring dynamically creates a proxy that implements the same interface(s) as the class you're annotating. And when clients make calls into your object, the calls are intercepted and the behaviors injected via the proxy mechanism.

What is @transactional annotation in Java?

Transactional annotation provides the application the ability to declaratively control transaction boundaries on CDI managed beans, as well as classes defined as managed beans by the Java EE specification, at both the class and method level where method level annotations override those at the class level.


2 Answers

From the Spring reference 2.5:

When using proxies, the @Transactional annotation should only be applied to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error will be raised, but the annotated method will not exhibit the configured transactional settings.

So Spring ignores @Transactional annotation on non-public methods.

Also,

In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional!

So even if you make your method public, calling it from within a method of same class will not start a new transaction.

You can use aspectj mode in transaction settings so that the transaction related code is weaved in the class and no proxy is created at runtime.

See the reference document for more details.

Another possible way of doing this is fetching the spring proxy of the class in the class itself and call methods on it rather than 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     }  } 
like image 179
Abhinav Sarkar Avatar answered Sep 20 '22 12:09

Abhinav Sarkar


@Transactional will only be noticed if it's on a public method, due to the way Spring AOP works.

However, you can programmatically start a new transaction if you desire, using TransactionTemplate, e.g.

TransactionTemplate txTemplate = new TransactionTemplate(txManager);                 txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); txTemplate.execute(status -> {         // do stuff }); 
like image 36
skaffman Avatar answered Sep 19 '22 12:09

skaffman