Reputation: 474
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
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