Spring的@Transactional在每个服务方法结束时提交

4
我已经在Vaadin、Spring项目中配置了常规的声明式事务管理。我在root-context.xml中添加了<tx:annotation-driven transaction-manager="transactionManager" />,并在pom中添加了所有其他必需的maven依赖项。我的Service方法使用默认传播级别注释了@Transactional
我想从UI端的一个方法中调用两个Service方法,期望这两个Service方法参与到单个事务中,因为默认传播级别是PROPAGATION_REQUIRED。但是这两个方法是独立地提交到数据库的。这意味着如果第二个方法失败,第一个方法已经提交到数据库了。我没有使用try{}catch{{块,所以任何RuntimeException都会被抛出。
附上了Spring日志。为了减少行数,删除了一些行。
[qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/getTransaction Creating new transaction with name [...UserServiceImpl.turnOnPwdResetFlag]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/doBegin Switching JDBC Connection [com.jolbox.bonecp.ConnectionHandle@42398a05] to manual commit [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/handleExistingTransaction Participating in existing transaction [qtp187048467-48] DEBUG o.s.jdbc.core.JdbcTemplate/doInStatement SQL update affected 1 rows [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/processCommit Initiating transaction commit [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/doCommit Committing JDBC transaction on Connection [com.jolbox.bonecp.ConnectionHandle@42398a05] [qtp187048467-48] DEBUG o.s.jdbc.datasource.DataSourceUtils/doReleaseConnection Returning JDBC Connection to DataSource ` ` [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/getTransaction Creating new transaction with name [...UserServiceImpl.turnOffPwdResetFlag]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT;  [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/handleExistingTransaction Participating in existing transaction [qtp187048467-48] DEBUG o.s.jdbc.datasource.DataSourceUtils/doReleaseConnection Returning JDBC Connection to DataSource 

1
你能否展示更多的代码? - dunni
2个回答

7
如果您想在两个方法调用中进行一次事务,请确保调用这两个方法的方法也有事务注释,例如:
@Transactional
public void callingMethod() {
    method1();
    method2();
}

@Transactional
public void method1() {
}

@Transactional
public void method2() {
}

那就意味着如果method1和method2是从UI相关类的方法中调用的,我必须在该特定方法上也加上@Transactional注释吗? - SashikaXP
是的。调用堆栈中带有Transactional注释的最顶层方法定义了事务边界。在我的例子中,当进入callingMethod时启动事务,在退出callingMethod时提交事务。如果我删除callingMethod上的Transactional注释,则行为类似于您的情况(method1和method2各自有一个事务)。 - dunni
通常在UI端注释一个方法并不是一件好事,有时甚至是不可能的。如果UI部分是Vaadin,则无法在UI中注释一个方法。但是我发现,正如你所说,如果在从UI调用callingMethod()时已经存在一个事务,则其他方法将按预期参与该事务。我使用Servlet过滤器找到了解决方案。稍后我会更新答案。 - SashikaXP
2
另一种方法是在您的服务层中创建一个方法,调用其他两个方法,并使用Transactional注释该新方法。 - dunni

2
通过 Spring 事务内部机制视图中的打开会话过滤器 的帮助,我想出了一个解决方案。
根据第一个链接,在 Spring MVC 上下文中,事务在控制器上下文中开始,服务层中的 @Transactional 方法随后参与、创建或在已有事务之上创建另一个适当的事务。但如果 UI 层不是 Spring MVC,则不会发生这种情况。

Spring transaction flow

如图所示Spring事务图像,事务顾问是决定是否提交事务或标记回滚的人。因此,如果我们只是让服务方法代理创建一个事务,在那个代理方法结束时,顾问会决定提交事务。为了克服这个问题,我们必须在两个服务方法调用之前创建一个事务,这样这两个方法将加入事务而不是自己提交。
Hibernate上下文中,Spring MVC支持OpenSessionInView,它为每个请求创建一个事务。我所做的是创建一个类似的servlet过滤器,在请求开始时创建一个事务,并在过滤器链结束时提交。这在Vaadin、Spring和JDBC环境中完美地工作。如果您担心在每个HTTP请求上获取Spring事务和数据库连接,请尝试使用LazyConnectionDataSourceProxy
在这里查看解决方案:我想要...使用JDBC、Spring事务和@Transactional

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接