【问题标题】:Spring MVC + Hibernate @Transactional not Rollinback after exceptionSpring MVC + Hibernate @Transactional 异常后不回滚
【发布时间】:2015-02-12 16:00:38
【问题描述】:

我正在尝试使用 Spring 创建一个 CMT,每次出现异常时回滚事务。我的问题是,当我从注释为@Transactional 的方法对数据库进行多次更改时,即使在某些操作后强制使用空指针,它也不会回滚操作。如有任何帮助,我将不胜感激。

按照代码:

<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>

我的控制器:

@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");
    }

我的服务:

@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);
        }
    }

我的 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);
        }
    }

}

【问题讨论】:

    标签: java spring hibernate spring-mvc transactions


    【解决方案1】:

    您的事务没有得到回滚,因为没有抛出异常,换句话说saveUpdateDeleteTest 正在捕获异常,这就是为什么 spring 事务代理无法检测到任何异常,因此没有回滚。删除 catch 块,您将看到事务将回滚。请注意 spring transaction 回滚遵循 EJB 约定,即

    虽然 EJB 默认行为是让 EJB 容器 在系统异常时自动回滚事务(通常 运行时异常),EJB CMT 不回滚事务 自动在应用程序异常(即,检查 java.rmi.RemoteException 以外的异常)。虽然春天 声明式事务管理的默认行为遵循 EJB 约定(仅在未经检查的异常上自动回滚),它 自定义它通常很有用。

    因此,在您的情况下,您需要自定义是否希望在任何异常时回滚事务,如下所示:

    @Transactional(rollbackFor = Exception.class)
    

    【讨论】:

    • 你是对的,但是为什么spring在收到NullPointerException时回滚事务,而在收到ServiceException时却不回滚,女巫是扩展Exception类的异常?是否有一些特定类型的 os exceptionios 必须抛出才能进行 Spring 回滚事务?
    • 找到了我的解决方案的最后一部分,我需要使用 @Transactional(rollbackFor = GenericException.class) 来指定我自己的异常,这些异常应该会导致 spring 回滚。谢谢 =)
    猜你喜欢
    • 2019-06-30
    • 2020-10-18
    • 1970-01-01
    • 2018-01-12
    • 2017-04-14
    • 1970-01-01
    • 2018-07-24
    • 2018-01-17
    • 1970-01-01
    相关资源
    最近更新 更多