【问题标题】:Spring @Transactional commits at the end of each service methodSpring @Transactional 在每个服务方法结束时提交
【发布时间】:2015-04-29 18:35:13
【问题描述】:

我在 Vaadin, Spring 项目中配置了一个常用的声明式事务管理。我在我的 root-context.xml 中添加了<tx:annotation-driven transaction-manager="transactionManager" />,并在 pom.xml 中添加了所有其他必需的 maven 依赖项。 我的 Service 方法使用 @Transactional 注释,默认传播。

我想从 UI 端的一个方法调用两个服务方法,期望这两个服务方法参与单个事务,因为默认传播是 PROPAGATION_REQUIRED。但是这两种方法是独立地提交给数据库的。这意味着如果第二种方法失败,第一种方法无论如何都会提交给数据库。我没有使用 try{}catch{{ 块,所以任何 RuntimeException 都会冒泡。

附上弹簧原木。删除了一些行以减少行数

[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 

【问题讨论】:

  • 你能显示更多代码吗?

标签: java spring jdbc vaadin spring-transactions


【解决方案1】:

如果你想对两个方法调用都有一个事务,你必须确保调用这两个方法的方法也有事务注释,比如这个例子:

@Transactional
public void callingMethod() {
    method1();
    method2();
}

@Transactional
public void method1() {
}

@Transactional
public void method2() {
}

【讨论】:

  • 这意味着如果从 UI 相关类中的方法调用 method1 和 method2,我还必须使用 @Transactional 注释该特定方法?
  • 是的。调用堆栈中带有 Transactional 注释的最顶层方法定义了事务边界。在我的示例中,事务在进入调用方法时开始,并在退出调用方法时提交。如果我要删除调用方法上的事务注释,那么行为就像你的一样(方法 1 的一个事务和方法 2 的另一个事务)。
  • 通常在ui端注解一个方法并不是一件好事,有时甚至是不可能的。在 ui 部分是 vaadin 的情况下,在 ui 中注释方法是不可能的。但是我发现正如你所说,当从ui调用callingMethod()时事务已经可用,其他方法只是按预期参与该事务。找到了使用 servlet 过滤器的解决方案。我稍后会更新答案
  • 另一种方法是在您的服务层中创建一个方法,该方法调用您的其他两个方法并使用 Transactional 注释该新方法。
【解决方案2】:

Spring transaction internalsOpen session in view filter 的帮助下,我想出了一个解决方案。

根据第一个链接,在 Spring MVC 上下文中,事务在控制器上下文中开始,然后服务层中的 @Transactional 方法参与、创建或在现有事务之上创建另一个适当的事务。但如果 UI 层不是 Spring MVC,则不会发生这种情况。

正如Spring transaction image 所示,事务顾问负责决定是提交事务还是标记回滚。因此,如果我们只是让服务方法代理来创建预先不存在的事务,那么在该代理方法结束时,顾问会决定提交事务。为了克服这个问题,我们必须在两个服务方法调用之前创建一个事务,所以这两个方法将加入事务而不是自己提交。

Hibernate 上下文中,Spring MVC 支持 OpenSessionInView,它为每个请求创建一个事务。我所做的是创建一个类似的 servlet 过滤器,以在请求开始时创建事务并在过滤器链结束时提交。这在 Vaadin、Spring 和 JDBC 环境中完美运行。如果您非常担心每次 HTTP 请求都会获取 Spring 事务并因此获取数据库连接,请尝试使用 LazyConnectionDataSourceProxy

在此处查看解决方案:I want to... Use JDBC, Spring transactions and @Transactional

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    • 2016-08-30
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多