Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Propagation.REQUIRES_NEW does not create a new transaction in Spring with JPA

Tags:

I have the following scenario. I am using JPA, Spring:

@Autowired SampleService service;  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void PerformLogic(LogicData data) throws SIASFaultMessage {     SampleObject so = createSampleObject();      try{         .//do some logic to persist things in data         .         .         persistData(data);         .         .         .           updateSampleObject(so);          }     catch(Exception){         updateSampleObject(so);              throw new SIASFaultMessage();     }  }  @Transactional(propagation = Propagation.REQUIRES_NEW) public createSampleObject() {     SampleObject so = new SampleObject();      .     .//initialize so     .      service.persist(so);             return so; }  @Transactional(propagation = Propagation.REQUIRES_NEW) public updateSampleObject(SampleObject so) {                    service.persist(so);             return so; } 

When everything works fine, data is persisted in database without problems. However, when an exception is thrown, I need that the method updateSampleObject(so) persist the information in the database. This is not what is happening. If an exception is thrown the method updateSampleObject gets rolled back also, which is not what I need. I need that these two methods (createSampleObject and updateSampleObject) get persisted all the time, no matter whether an exception got thrown or not. How can I achieve this?

Moreover, if I anotate the methods createSampleObject and updateSampleObject with:

@Transactional(propagation = Propagation.NEVER) 

the idea is that an exception is thrown and I get no exception thrown. Where is the problem? Analizing the logs I see this line:

org.springframework.orm.jpa.JpaTransactionManager  ==> Creating new transaction with name [com.test.PerformLogic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT.... 

which means this transaction is created, but I see no hint of the other transaction.

This is the part of my configuration file for Spring regarding transactions

<bean id="myDataSource"       class="org.springframework.jdbc.datasource.DriverManagerDataSource">     <property name="driverClassName" value="${jdbc.driverClassName}"/>     <property name="url" value="${jdbc.url}"/>     <property name="username" value="${jdbc.username}"/>     <property name="password" value="${jdbc.password}"/> </bean> <bean id="entityManagerFactory"       class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">     <property name="dataSource" ref="myDataSource"/>     <property name="packagesToScan" value="cu.jpa"/>     <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>     <property name="jpaProperties">         <props>             <prop key="hibernate.dialect">${hibernate.dialect}</prop>             <prop key="hibernate.show_sql">true</prop>             <prop key="hibernate.hbm2ddl.auto">${hdm2ddl.auto}</prop>         </props>     </property>     <property value="/META-INF/jpa-persistence.xml" name="persistenceXmlLocation"/>     <property name="persistenceUnitName" value="jpaPersistenceUnit"/> </bean>  <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">     <property name="entityManagerFactory" ref="entityManagerFactory"/>     <property name="nestedTransactionAllowed" value="true" /> </bean>  <tx:annotation-driven transaction-manager="transactionManager"/> 
like image 420
Alfredo A. Avatar asked Feb 12 '15 14:02

Alfredo A.


People also ask

What is propagation required in Spring transaction?

Propagation. REQUIRED is the default setting of a @Transactional annotation. The REQUIRED propagation can be interpreted as follows: If there is no existing physical transaction, then the Spring container will create one.

What is @transactional propagation propagation Requires_new?

When the propagation is REQUIRES_NEW, Spring suspends the current transaction if it exists, and then creates a new one: @Transactional(propagation = Propagation.

What happens when a method annotated with @transaction propagation Requires_new is invoked?

The main difference between them is if a method in Spring Business Activity/DAO class is annotated with Propagation. REQUIRES_NEW, then when the execution comes to this method, it will create a new transaction irrespective of the existing transaction whereas if the method is annotated with Propagation.

Which propagation level describe support a current transaction create a new one if none exists?

Enum Constant Summary. Support a current transaction, throw an exception if none exists.

What is propagation in spring transaction?

Transaction Propagation Propagation defines our business logic's transaction boundary. Spring manages to start and pause a transaction according to our propagation setting. Spring calls TransactionManager::getTransaction to get or create a transaction according to the propagation.

What happens when propagation is required new in Spring Boot?

When the propagation is REQUIRES_NEW, Spring suspends the current transaction if it exists, and then creates a new one: @Transactional (propagation = Propagation.REQUIRES_NEW) public void requiresNewExample(String user) { // ... }

When does spring throw an exception when propagation is mandatory?

When the propagation is MANDATORY, if there is an active transaction, then it will be used. If there isn't an active transaction, then Spring throws an exception: @Transactional (propagation = Propagation.MANDATORY) public void mandatoryExample(String user) { // ... } Let's again see the pseudo-code: 3.4. NEVER Propagation

Does jpatransactionmanager support nested propagation?

If there's no active transaction, it works like REQUIRED. DataSourceTransactionManager supports this propagation out-of-the-box. Some implementations of JTATransactionManager may also support this. JpaTransactionManager supports NESTED only for JDBC connections.


2 Answers

Spring transactions are proxy-based. Here's thus how it works when bean A causes a transactional of bean B. A has in fact a reference to a proxy, which delegates to the bean B. This proxy is the one which starts and commits/rollbacks the transaction:

A ---> proxy ---> B 

In your code, a transactional method of A calls another transactional method of A. So Spring can't intercept the call and start a new transaction. It's a regular method call without any proxy involved.

So, if you want a new transaction to start, the method createSampleObject() should be in another bean, injected into your current bean.

This is explained with more details in the documentation.

like image 131
JB Nizet Avatar answered Oct 14 '22 17:10

JB Nizet


My guess is that since both methods are in the same bean, the Spring's AOP does not have a chance to intercept the create/updateSampleObject method calls. Try moving the methods to a separate bean.

like image 20
zmitrok Avatar answered Oct 14 '22 18:10

zmitrok