【问题标题】:Spring Hibernate transaction exception rollbackSpring Hibernate 事务异常回滚
【发布时间】:2011-12-08 20:00:34
【问题描述】:

2011 年 12 月 12 日更新

应用程序正在使用一个通用 DAO 类,该类可以在 here 找到。

基于重新阅读spring manual@Repository 的使用情况,看来我需要将其添加到我的 GenericDAO 实现中,以便 spring 处理异常转换。

我将@Repository 添加到 GenericDAO,但结果没有改变。父对象仍被持久化到数据库中。


使用 Spring 2.5.6、JBoss 5.1 及其休眠版本、Spring MVC、MySQL

我正在做一些测试,以确保我的事务在发生异常时能够正确回滚。在下面的代码中,第二个方法:createProfile 将抛出一个 NullPointerException,从而阻止该方法完成。我曾预计交易会被回滚。我发现用户对象被持久化到数据库中。我想要的是,如果第二个操作失败,那么第一个操作也需要回滚。

我做了一些更改,将父方法的传播更改为 REQUIRED_NEW,但没有看到行为发生任何变化。

我已开启 Spring 事务的日志记录。

我一直在这里查看不同的帖子并查看what spring has to say on transactions as well。我似乎在交易行为方面遗漏了一些东西。

我在这里做错了什么。

我有以下定义

    @Transactional(propagation = Propagation.SUPPORTS)
    public class ProfileServiceImpl implements ProfileService
    {

      @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
      public boolean createProfile(UserDTO pUserDTO, ContactInfoDTO pContactInfoDTO)
        {
            boolean retVal = false;

            if(this.createProfile(pUserDTO))
            {
                if(this.addAddressToUser(pContactInfoDTO, pUserDTO.getId()))
                {
                    retVal = true;
                }
            }
            return retVal;
        }

    @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
    public boolean createProfile(UserDTO userDTO)
    {
        boolean retVal = false;

        if(this.getProfileQueries().isLoginUnique(userDTO.getLogin(),userDTO.getSiteInfoId()))
        {
            User user = new User();
            BeanUtils.copyProperties(userDTO, user);
            user.setPassword(this.stringDigester.digest(userDTO.getPassword()));
            user.setSiteInfo(this.getSiteInfoDao().read(userDTO.getSiteInfoId()));
            user.setSecurityLevel(SecurityLevel.ROLE_USER);
            user.setStatus(Status.ACTIVE);

            Long pk = this.getUserDao().create(user);
            userDTO.setId(pk);

            retVal = true;
        }
        return retVal;
    }
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
    public boolean addAddressToUser(ContactInfoDTO pAddress, Long pProfileId)
    {
        boolean retVal = false;

        if(null != pAddress && null != pProfileId)
        {
            ContactInfo contactInfo = new ContactInfo();
            BeanUtils.copyProperties(pAddress, contactInfo);

            User user = this.getUserDao().read(pProfileId);
            contactInfo.setUser(user);
            this.getContactInfoDao().create(contactInfo);

            user.getUserAddress().setProfileAddress(contactInfo);
            user.getUserAddress().setBillingAddress(contactInfo);
            this.getUserDao().update(user);

            retVal = true;

        }
        return retVal;
    }
}

数据配置

      <!-- TRANSACTION MANAGER--> 
      <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
      </bean>

      <!-- ANNOTATION DRIVEN TRANSACTIONS -->
      <tx:annotation-driven transaction-manager="transactionManager" />


  <bean id="jndiDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:/MySqlDS"/>
  </bean>

  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="jndiDataSource"/>
    <property name="annotatedClasses">
      <list>
        <value>com.vsg.dataaccess.user.entity.User</value>
        <value>com.vsg.dataaccess.user.entity.ContactInfo</value>
        <value>com.vsg.dataaccess.user.entity.UserAddress</value>
     </list>
    </property>


    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        <prop key="hibernate.hbm2ddl.auto">update</prop>
        <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
        <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
        <prop key="hibernate.use_sql_comments">${hibernate.use_sql_comments}</prop>
        <prop key="hibernate.jdbc.batch_size">20</prop>
        <prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</prop>
        <prop key="net.sf.ehcache.configurationResourceName">ehcache.xml</prop>
        <prop key="hibernate.cache.use_second_level_cache">true</prop>
        <prop key="hibernate.cache.use_structured_entries">true</prop>
        <prop key="hibernate.cache.use_query_cache">true</prop>
        <prop key="hibernate.generate_statistics">true</prop>
        <prop key="org.hibernate.envers.audit_table_suffix">_aud</prop>
        <prop key="org.hibernate.envers.revision_field_name">rev_number</prop>
        <prop key="org.hibernate.envers.revision_type_field_name">rev_type</prop>
        <prop key="org.hibernate.envers.revision_on_collection_change">true</prop>        
      </props>
    </property>
    <property name="entityInterceptor">
      <bean class="vsg.ecotrak.dataaccess.framework.hibernate.interceptor.AuditTrailInterceptor"/>
    </property>
  </bean>

【问题讨论】:

  • 需要看看你实际上是如何调用该方法的。会猜测该服务正在自我调用它并在它实际跨越真正的代理边界之前捕获异常。 (或者上下文设置错误并且事务管理器没有连接到实体管理器工厂,因此无论如何它都会在自动提交模式下创建用户)
  • 方法被控制器类调用
  • 您能否为 UserDao 添加代码 - 您如何管理休眠会话。你在使用hibernateTemplate吗?
  • UserDAO 使用的是通用 dao 类。 hibernateTemplate 没有被使用。我已经更新了我的原始信息以反映 dao 层的处理方式。

标签: java spring transactions spring-transactions


【解决方案1】:

确保(在 IDE 中的调试模式下)类 ProfileServiceImpl 的实例被 Spring 的 AOP 代理包装(这意味着 Spring Transaction 配置正确),还请提供您的事务管理器和数据源的 *.xml 配置(等.)

【讨论】:

    【解决方案2】:

    我认为自动提交是导致问题的原因。如果您想在第二个查询失败时回滚第一个查询,您可能需要关闭他并手动管理事务。否则第一个查询将被自动提交。想想这个。

    【讨论】:

      猜你喜欢
      • 2016-08-14
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-09
      • 2018-07-24
      • 2013-10-16
      相关资源
      最近更新 更多