Spring MVC + Hibernate @Transactional not Rollinback after exception

I am trying to create a CMT with Spring that rolls back transactions every time an exception is thrown. My problem is that when I have multiple changes to the database from a method annotated as @Transactional, it does not roll back operations, even when I force a null pointer after some operations. I would 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("r@gmail.com");
        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);
        }
    }

}

      

+3


source to share


1 answer


Your transaction does not get rolled back because there is no exception, in other words it is saveUpdateDeleteTest

catching the exception, so the spring transactional proxy cannot catch any exception and therefore no rollback. Remove the blocking block and you will see that the transaction will be rolled back. PLease please note that spring transaction rollback follows EJB Conventation ie

Whereas the default EJB behavior for an EJB container is to automatically rollback a transaction on a system exception (usually a runtime exception), an EJB CMT does not automatically rollback a transaction when an application exception is thrown (i.e. checking for an exception other than java.rmi.RemoteException) ... While Spring's default behavior for declarative transaction management follows the EJB convention (rollback is done automatically on unchecked exceptions only), it is often useful to tweak this.



So, in your case, you need to configure whether you want the transaction to be rolled back on any exception, for example:

@Transactional(rollbackFor = Exception.class)

      

+1


source







All Articles