Roger that
Roger that

Reputation: 474

Spring MVC + Hibernate @Transactional not Rollinback after exception

I'm trying to create a CMT with Spring that rollback the transactions each time an exception rises. My problem is that when I have multiple changes in the database from an method annotated as @Transactional, it's not rolling back the operations even when I force a null pointer after some operations. I'll appreciate any help.

Follow the code:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:p="http://www.springframework.org/schema/p" 
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
 http://www.springframework.org/schema/mvc    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
 http://www.springframework.org/schema/tx     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

     <context:annotation-config></context:annotation-config>

     <context:component-scan base-package="br.com.test" />
     <mvc:annotation-driven/> 

     <tx:annotation-driven transaction-manager="transactionManager" />

     <context:property-placeholder location="classpath:config.properties"></context:property-placeholder> 

     <bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource" p:basename="Messages">
     </bean>

     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
         <property name="prefix">
            <value>/WEB-INF/jsp/</value>
         </property>
         <property name="suffix">
            <value>.jsp</value>
         </property>
     </bean>

     <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" 
        p:driverClassName="${jdbc.driverClassName}" 
        p:password="${jdbc.password}" 
        p:url="${jdbc.url}" 
        p:username="${jdbc.username}">
     </bean> 

     <bean  id="transactionManager" 
            class="org.springframework.orm.hibernate3.HibernateTransactionManager" 
            p:sessionFactory-ref="sessionFactory" />

      <bean class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" 
         id="sessionFactory">
         <property name="dataSource" ref="dataSource"></property>
         <property name="hibernateProperties">
           <props>       
                 <prop key="hibernate.dialect">${hibernate.dialect}</prop>         
                 <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            </props>
         </property>
        <property name="packagesToScan" value="br.com.test"></property>
     </bean>


     <mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />
</beans>

My Controller:

@Controller
public class UserController {

    @Resource(name="userService")
    private UserService userService;

    @RequestMapping(value="/user/create", method=RequestMethod.GET)
    public ModelAndView createUser() throws GenericException {

        this.userService.saveUpdateDeleteTest();    

        return new ModelAndView("createUser");
    }

My Service:

@Service("userService")
public class UserServiceImpl implements UserService {

    @Resource(name="userDao")
    private UserDao userDao;

    @Override
    @Transactional
    public void saveUpdateDeleteTest() throws GenericException {
        User user = new User();
        user.setFirstName("Rogerio");
        user.setLastName("R");
        user.setEmail("[email protected]");
        user.setPassword("123");
        try {
            this.userDao.save(user);

            User u = this.userDao.findById(12l);
            u.setFirstName("changed");
            this.userDao.save(u);

            User u2 = this.userDao.findById(11l);
            this.userDao.delete(u2);
            u2 = null;
            u2.getCreated(); //Forcing a null pointer here
        } catch (GenericException ge) {
            throw ge;
        } catch (Exception e) {
            throw new ServiceException("Error at saving user. " + e.getMessage(), e);
        }
    }

My DAO:

@Repository("userDao")
public class UserDaoImpl implements UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    public void save(User user) throws DaoException {
        try {
            if (user.getId() == null) {
                user.setCreated(new Date());
            } else {
                user.setLastUpdated(new Date());
            }
            this.sessionFactory.getCurrentSession().saveOrUpdate(user);
        } catch (Exception e) {
            throw new DaoException("Error at saving user.", e);
        }
    }

    public void delete(User user) throws DaoException {
        try {
            this.sessionFactory.getCurrentSession().delete(user);
        } catch (Exception e) {
            throw new DaoException("Data access error. " + e.getMessage(), e);
        }
    }

    public User findById(Long id) throws DaoException {
        try {
            Query query = this.sessionFactory.getCurrentSession().createQuery(
                    "from User u where u.id = :id");
            query.setLong("id", id);

            List<User> list = query.list();
            User returnedObject = null;

            if (list != null && list.size() > 0) {
                returnedObject = list.iterator().next();
            }

            return returnedObject;
        } catch (Exception e) {
            throw new DaoException("Data access error. " + e.getMessage(), e);
        }
    }

}

Upvotes: 3

Views: 1163

Answers (1)

sol4me
sol4me

Reputation: 15708

Your transaction is not getting rollback because there is no exception thrown , in other words saveUpdateDeleteTest is catching the exception, thats why spring transactional proxy cannot detect any exception and Hence no rollback. Remove the catch block and you will see that transaction will rollback . PLease note that spring transaction rollback follows EJB Conventation i.e.

While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.

So in your case you need to customize if you want the transaction to be rolled back on any exception, like this:

@Transactional(rollbackFor = Exception.class)

Upvotes: 1

Related Questions