问题
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>
回答1:
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
回答2:
you need to use <tx:annotation-driven/>
inside you spring configuration file to enable the annotation driven transaction management.
回答3:
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" />
回答4:
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
来源:https://stackoverflow.com/questions/40173587/spring-transaction-not-rolling-back