Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Transaction - automatic rollback of previous db updates when one db update failes

I am writing a simple application (Spring + Hibernate + PostgreSql db). I am just trying to construct a sample object and persist in db.

I run a simple java class main method where i have loaded the applicationContext and have got reference to the service class as below

TestService srv = (TestService)factory.getBean("testService");  

Application Context - context :

<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactoryVsm" />
</bean>

<bean id="testService" class="com.test.service.TestServiceImpl">
    <property name="testDao" ref="testDao"/>
</bean>

<bean id="testDao" class="com.test.dao.TestDaoImpl>
    <property name="sessionFactory" ref="sessionFactoryVsm"/>
</bean>

In TestService I have injected TestDao. In test service method I have constructed to employee objects emp1 and emp2 and calling dao twice to update.

TestDaoImpl code:

public void saveOrUpdate(BaseDomainModel baseObject) {

    Session session = null;
    try {
        session = getHibernateTemplate().getSessionFactory().openSession();
        session.saveOrUpdate(baseObject);
        session.flush();
    } catch (Exception e) {
        logger.error("Generic DAO:saveOrUpdate::" + e);
        e.printStackTrace();
    } finally {
        if (session != null) {
            session.close();
        }
    }

}

When emp2 update fails emp1 should also fail. How do I do that. Please advice

Thanks in advance

Updated :

Thanks Nanda. I tried Declarative transaction. But it is not working. emp1 gets persisted and not rolled back eveb second dao call fails. I have added transaction advice to the method.

to test if the transaction advice is applied or not i changed the propagation to "NOT_SUPPORTED". but still emp1 gets persisted. the expectation is we should have got Transaction Not Supported type of exception. please advice .

UPDATED

@seanizer - Thanks for the update. I have even tried adding

@Transactional(propagation=Propagation.NOT_SUPPORTED)
public void saveEmp(Employee emp)

to that service method. But it didn't work. Moreover iterating the collection<BaseDomainModel> hold good only if i need to call one dao. If in case i have to call two different dao to persist obj1 and obj2- this may not help. Just to check if the transaction is getting applied I get @Transactional(propagation=Propagation.NOT_SUPPORTED). But still obj1 got persisted. I just doubt if the xml configuration/ annotation given is correct. please check

<bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactoryVsm" />
    </bean>

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
         <tx:method name="saveEmp" propagation="REQUIRED" rollback-for="Exception"/>
        <tx:method name="*"/>
    </tx:attributes>
 </tx:advice>   
 <aop:config>
    <aop:pointcut id="testServiceOperation" expression="execution(*com.test.service.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="testServiceOperation"/>
  </aop:config> 

I am using org.springframework.orm.hibernate3.HibernateTransactionManager for the transactionManager. Is this correct ?


Updated

I have created my exception class myRuntimeExp extending from RuntimeException and throwing the same from Dao method to the service method. but still the rollback is not happening. I just doubt if I have correctly given the configurations in the applnContext.xml. Can someone help me how to check if the transaction advice / annotation is being applied to the method or not? is there any way of running it in a debugging mode and check

Issue :

I was using

session = getHibernateTemplate().getSessionFactory().openSession();

But it should be current session and it is working fine.

session = getHibernateTemplate().getSessionFactory().getCurrentSession();
like image 793
Vaandu Avatar asked Oct 07 '10 10:10

Vaandu


1 Answers

If you use declarative transaction management, you can lose most of this boilerplate:

TestDaoImpl:

private SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory f){
    this.sessionFactory = f;
}

public void saveOrUpdate(BaseDomainModel baseObject) {
    Session session = sessionFactory.getCurrentSession();
    session.saveOrUpdate(baseObject);
}

And you can control the transaction handling from the service layer using @Transactional (or xml configuration)

TestServiceImpl:

private TestDao testDao;

public void setTestDao(TestDao  d){
    this.testDao = d;
}

@Transactional // one transaction for multiple operations
public void someServiceMethod(Collection<BaseDomainModel> data){
     for(BaseDomainModel baseObject : data)
         testDao.saveOrUpdate(baseObject);
}

Reference:

like image 96
Sean Patrick Floyd Avatar answered Sep 25 '22 22:09

Sean Patrick Floyd