简体   繁体   中英

Spring Transaction not rolling back

I have something like this:

@Service
@Transactional
public class ServiceA {

    @Autowired
    SomeDAO1 dao1; 

    @Autowired
    ServiceB serviceB;

    public void methodServiceA() {

        serviceB.someMethodThatRunsInsertIntoDB(); 
        dao1.anotherMethodThatRunsInsertIntoDB(); 

    }

}

@Service
@Transactional
public class ServiceB {

     @Autowired
     Dao2 dao2;

     public void someMethodThatRunsInsertIntoDB() {
          dao2.insertXXX();
     }

}

My problem is: if serviceB.someMethodThatRunsInsertIntoDB() executes sucessfully but dao1.anotherMethodThatRunsInsertIntoDB() throw an exception, the changes made by serviceB are not rolled back. I need to rollback those changes in case an exception occur in dao1.anotherMethodThatRunsInsertIntoDB() . How can I do this?

// EDITED

Transaction configuration in spring-servlet.xml

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="jpaDialect">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
    </property>
</bean>

Is it relevant if one dao uses an EntityManager and the other dao uses JdbcTemplate to interact with DB?

//UPDATE -- EntityManager configuration

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="generateDdl" value="true" />
        </bean>
    </property>

you need to pass rollbackFor parameter with type of your checked exception. It seems that spring rollbacks only on unchecked exceptions by default. More details: Spring transaction: rollback on Exception or Throwable

你需要在spring配置文件中使用<tx:annotation-driven/>来启用注释驱动的事务管理。

It's because your dao1.anotherMethodThatRunsInsertIntoDB() call does not support current transaction (ServiceA transaction).

You need to use below propagation level in your ServiceB class.

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class ServiceB {

REQUIRED: Spring REQUIRED behavior means that the same transaction will be used if there is an already opened transaction in the current bean method execution context. Create a new one if none exists. In short this means that if an inner(2nd Transaction) method causes a transaction to rollback, the outer(1st Transaction) method will fail to commit and will also rollback the transaction.

Propagation Means: Typically, all code executed within a transaction scope will run in that transaction. However, you have the option of specifying the behavior in the event that a transactional method is executed when a transaction context already exists. For example, code can continue running in the existing transaction (the common case); or the existing transaction can be suspended and a new transaction created. Spring offers all of the transaction propagation options familiar from EJB CMT. To read about the semantics of transaction propagation in Spring, see Transaction Propagation

Edited
Note: The only exceptions that set a transaction to rollback state by default are the unchecked exceptions (like RuntimeException). If you want checked exceptions to also set transactions to rollback you must configure them to do so, eg.

@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = YourCheckedException.class))
public class ServiceA {

Note: As I noticed, declarative transaction management is AOP based. This means that Spring wraps the transactional beans into a transactional proxy, which takes care of starting and committing transactions. This means that the method call must be intercepted by the proxy in order to be transactional. You need to make sure below config had in your Spring configuration file.

<tx:annotation-driven transaction-manager="transactionManager" />

My first suggestion is to use @Transactional annotation on method level if it is really not needed on class level.

Second thing, try use javax.transaction.Transactional annotation instead of org.springframework.transaction.annotation.Transactional , Spring will automatically handle the propagation.

You also need to enable transaction management before using @Transactional , Using Spring Boot we can simply do that by marking Application class by @EnableTransactionManagement .

However, you can surely do this by XML configuration ( <tx:annotation-driven /> ) as well if you want to. Read http://docs.spring.io/spring-data/jpa/docs/1.11.0.M1/reference/html/#transactions for more details

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM