回滚对我的独立Spring应用程序不起作用

时间:2011-05-06 07:38:53

标签: spring transactions rollback

我正在开发一个独立的spring / jpa / hibernate应用程序。

我遇到的问题是,即使引发了RuntimeException,我的应用也不会回滚事务。

这是我的配置:

 <context:annotation-config />
    <context:component-scan base-package="com.jeanbaptistemartin"/>
    <context:property-placeholder location="classpath:application.properties"/>
    <bean id="gestionnaireMailing" class="com.jeanbaptistemartin.desktop.JFrameGestionnaireMailing" init-method="init" >

    </bean>

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml"/>
        <property name="shared" value="true"/>
    </bean>

    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="${mail.server}"/>
        <property name="port" value="${mail.port}"/>
        <property name="javaMailProperties">
            <props>
                <prop key="mail.smtp.connectiontimeout">2000</prop>    
                <prop key="mail.smtp.timeout">2000</prop>    
            </props>    
        </property>
    </bean>
    <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
        <property name="velocityProperties">
            <value>
            resource.loader=class
            class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
            </value>
        </property>
    </bean>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="jbmPU" />
        <property name="persistenceXmlLocation" value="classpath:/META-INF/persistence.xml" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="${database.showSql}" />
                <property name="generateDdl" value="${database.generateDdl}"/>
                <property name="databasePlatform" value="${database.dialect}"/>
            </bean>
        </property>
    </bean>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${database.driver}"/>
        <property name="jdbcUrl" value="${database.url}"/>
        <property name="user" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
        <property name="minPoolSize" value="5" />
        <property name="maxPoolSize" value="20" />
        <property name="idleConnectionTestPeriod" value="3000" />
        <property name="loginTimeout" value="300" />
    </bean>
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />

我的交易方法:

 @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {RuntimeException.class})
    public boolean mailAbonne(List<Sculpture> sculpturesChoisiesPourMailing, Abonne abonne) {
        try {
            for (Sculpture sculpture : sculpturesChoisiesPourMailing) {
                MailingAbonnePK mapk = new MailingAbonnePK(sculpture.getSculptureID(), abonne.getAbonneID());
                MailingAbonne ma = new MailingAbonne(mapk, new Date());
                dao.persistMailingAbonnee(ma);
            }
            envoyerMail(sculpturesChoisiesPourMailing, abonne);//this method sometimes throws a RuntimeException.
            return true;
        } catch (RuntimeException e) {
            log.error("Exception");
            log.error(e);
            throw new RuntimeException();
        }
    }

在我的道:

@PersistenceContext(type = PersistenceContextType.TRANSACTION)
    private EntityManager entityManager;

现在关于我的应用的当前行为:

当envoyerMail引发RuntimeException或其子类时,应用程序会无限期挂起。

现在关于我的应用程序的期望行为的另一个词。

我的mailAbonne方法在循环中调用如下:

 for (Abonne abonne : totalAbonnes) {
   mailAbonne(sculpturesChoisiesPourMailing, abonne);
}

理想情况下,我希望循环的一次迭代失败或原子地成功,即如果在总共5次迭代中的迭代3中引发RuntimeException,那么我将在我的数据库数据中对应于4次成功迭代,对应于失败迭代的数据将被回滚。

有人可以帮忙吗?

学家

1 个答案:

答案 0 :(得分:5)

首先,您无需指定rollbackFor = {RuntimeException.class}。默认行为是回滚任何运行时异常。

您的问题似乎是您从同一个bean的另一个方法调用事务方法。 Spring自动启动和停止事务,因为它将每个bean包装在处理此事务工作的代理中。当您从同一个bean调用方法时,代理无法拦截该调用并为您启动/停止事务。因此,您应该将事务方法放在另一个bean中。

然后进行迭代。为了工作,你需要

  • 使包含循环的方法不是事务性的,以便每次调用mailAbonne都会启动一个新事务
  • 或传播mailAbonne方法REQUIRES_NEW,以使其拥有自己的独立交易

当然,您还需要在try / catch块内的循环中包装mailAbonne的每个调用,以便捕获运行时异常,并且即使当前的一个失败,也可以完成对mailAbonne的下一次调用。