问题
I'm new to Spring and I'm not able to make the transaction propagation in "REQUIRED" mode.
Here is an example:
@Controller
public class ExampleController
{
@Autowired
Foo foo;
@Autowired
Bar bar;
@RequestMapping(value = "/example")
public String submitForm(Model model) throws Exception
{
User user = new User("Joe", "Bloggs");
user = foo.save(user);
bar.simpleMethod(user);
return "success";
}
}
@Repository
public class Foo
{
// A JPA repository
private EntityManager em;
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public User save(User user)
{
return this.em.merge(user);
}
@PersistenceContext
void setEntityManager(EntityManager entityManager)
{
this.em = entityManager;
}
}
public class Bar
{
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void simpleMethod(User user)
{
// Do something...
}
}
The applicationContext.xml (important bits):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:property-placeholder location="/WEB-INF/jdbc.properties" />
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.driverClassName}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
</constructor-arg>
</bean>
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="showSql" value="${db.showSql}" />
<property name="generateDdl" value="${db.generateDdl}" />
</bean>
<!-- enabling annotation driven configuration /-->
<context:annotation-config />
<context:component-scan base-package="my.package" />
<!-- Instructs the container to look for beans with @Transactional and decorate them -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- FactoryBean that creates the EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jpaVendorAdapter" ref="jpaAdapter" />
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<property name="dataSource" ref="dataSource" />
</bean>
<!-- A transaction manager for working with JPA EntityManagerFactories -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
If an exception occours in bar.simpleMethod()
, foo.save(...)
is not rolled back (or maybe it is, but the database is certainly not). Does anyone know why?
回答1:
If an exception occours in
bar.simpleMethod()
,foo.save(...)
is not rolled back
And neither it should be - you've wrapped your transaction annotations individually around Bar.simpleMethod
and Foo.save
, and so these will be performed as two separate transactions. If Bar.simpleMethod
fails, it rolls back its own transaction, but Foo.save
's transaction has already been committed. There's no single transaction covering both.
You need to encapsulate the two operations within a method that performs both operations in a single transaction. This is best done by introducing an extra component, which is invoked by the controller. This method would be annotated with @Transactional
, and performs Bar.simpleMethod
and Foo.save
. The @Transactional
annotations on Bar.simpleMethod
and Foo.save
will be made part of the same overall transaction.
回答2:
You've got two independent transactions here. The first one is committed, the second one is rolled back upon exception. Nothing surprising here really. To make those two calls participate in the same transaction, you should place them in a single method annotated with @Transactional
.
来源:https://stackoverflow.com/questions/9276037/transaction-required-propagation-in-spring