交易“需要”春季繁殖
我是 Spring 新手,无法在“REQUIRED”模式下进行事务传播。
下面是一个示例:
@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...
}
}
applicationContext.xml(重要位):
<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>
如果 bar.simpleMethod()
中发生异常,则不会滚动 foo.save(...)
返回(或者也许是,但数据库肯定不是)。有谁知道为什么?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
,也不应该回滚 - 您已经包装了事务注释分别围绕
Bar.simpleMethod
和Foo.save
,因此它们将作为两个单独的事务执行。如果Bar.simpleMethod
失败,它会回滚自己的事务,但Foo.save
的事务已经提交。没有单一交易涵盖这两者。您需要将这两个操作封装在一个方法中,该方法在单个事务中执行这两个操作。最好通过引入一个由控制器调用的额外组件来完成。该方法将使用
@Transactional
进行注释,并执行Bar.simpleMethod
和Foo.save
。Bar.simpleMethod
和Foo.save
上的@Transactional
注释将成为同一整体事务的一部分。And neither it should be - you've wrapped your transaction annotations individually around
Bar.simpleMethod
andFoo.save
, and so these will be performed as two separate transactions. IfBar.simpleMethod
fails, it rolls back its own transaction, butFoo.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 performsBar.simpleMethod
andFoo.save
. The@Transactional
annotations onBar.simpleMethod
andFoo.save
will be made part of the same overall transaction.这里有两个独立的交易。第一个已提交,第二个在异常时回滚。这里真的没什么奇怪的。要使这两个调用参与同一事务,您应该将它们放在一个用
@Transactional
注释的方法中。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
.