maqjav maqjav - 1 month ago 7
Java Question

Avoid Transaction rollback in Spring

Assuming I have the next code:

@Autowired
private IManager1 manager1;

@Autowired
private IManager2 manager2;

@Autowired
private IManager3 manager3;

@Transactional
public void run() {
manager1.doStuff();
manager2.registerStuffDone();

manager3.doStuff();
manager2.registerStuffDone();

manager1.doMoreStuff();
manager2.registerStuffDone();
}


If any exception is launched I want to rollback everything done by the "doStuff()" methods, but I don't want to rollback the data recorded by the "registerStuffDone()" method.

I've been reading the propagation options for @Transactional annotation, but I don't understand how to use them properly.

Every manager internally uses hiberante to commit the changes:

@Autowired
private IManager1Dao manager1Dao;

@Transactional
public void doStuff() {
manager1Dao.doStuff();
}


Where the dao looks like this:

@PersistenceContext
protected EntityManager entityManager;

public void doStuff() {
MyObject whatever = doThings();
entityManager.merge(whatever);
}


This is my applicationContext configuration:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourcePool" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
</bean>

<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>


Ideas?

Answer

You need 2 transactions, one for the stuff to be committed and one for the stuff to be rolled back.

   @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor={Exception1.class, Exception2.class})
public void registerStuffDone()() {
   //code
}

Your run method will then use the first transaction and that will be rolled back, but the registerStuffDone method will start a second transaction which will be commited.