4

想象一个使用 spring、jdbc 和 aop 的事务性多线程 Java 应用程序,其中 m 个包中的 n 个类都参与数据库事务。现在假设需要在一个事务中定义任意一组类。此外,范围内总是有一个类 T 在调用时提交事务。

为了清楚起见,让我举一个例子:给定包 A、B、Z 和类 A.Foo、B.Bar 和 ZT 调用各个类的以下实例(可能由不同的调用者以及介于两者之间的其他类): Foo,B.Bar,A.Foo,ZT 只有在调用 ZT 之后才会提交事务。如果应用程序因任何原因关闭,除非涉及 ZT,否则将永远不会提交事务。

实例可以相互调用,并且如前所述,没有通用的入口点从单个入口点(如服务层)调用所有实例,这将成为 Spring 事务标签的简单目标。

现在的问题是:可以使用方面解决这个问题吗?如果是这样,基本方法是什么?谢谢。

4

3 回答 3

2

Spring 的习惯用法会建议有一个了解工作单元的服务接口和一个处理关系数据库的持久性接口。服务接口中的方法应该与您的用例密切相关。服务实现了解实现用例目标所需的所有模型和持久性包和类。

“实例可以相互调用,正如已经提到的,没有一个通用的入口点可以从单个入口点(如服务层)调用所有实例,这将成为 Spring 事务标签的简单目标。”

这句话告诉我,您正在以一种不太容易适应 Spring 习语的方式做事。很难确切地说出您想要什么,但听起来您正在抛弃 Spring 推荐的两个最重要的层。如果看起来很难违背常规,也许是您的设计需要返工。

“......不同的调用者之间有其他类......” - 也许您需要在这些调用者上单独声明事务。

您可以使用 Spring AOP 或 AspectJ 在 XML 配置中声明事务。Spring 2.5 及更高版本现在为您提供使用注解的选项,如果您更喜欢 XML 配置。

你的描述让我非常困惑。也许这也是您遇到困难的部分原因。我会重新考虑或澄清。

于 2009-06-16T00:54:38.107 回答
2

您不需要单个入口点,但您确实需要将事务拦截器应用于所有入口点的能力,以便重入调用可以参与同一事务。假设您可以做到这一点,您可以使用 ThreadLocal 标志和自定义org.springframework.transaction.support.TransactionSynchronization实现来完成此操作。

当提交可以安全进行时,您将修改 ZT 以设置 ThreadLocal 标志。在TransactionSynchronization.beforeCommit()从 调用的实现中PlatformTransactionManager,您可以检查标志并使用它来确定是否允许提交继续进行。RuntimeException如果标志不存在,您可以通过抛出 a 来强制回滚。

一个警告是,如果您有其他类型的事务(不涉及您描述的 3 个协调类),您需要确保它们不会无意中回滚。为此,您可以通过另一个 ThreadLocal 标志在 A.Foo、B.Bar 和 ZT 中标记此“特殊事务”,然后在上述beforeCommit()方法的保护子句中检查该标志。伪代码:


void beforeCommit() {
  if in special transaction
    if commit flag not set
       throw new RuntimeException("cancel transaction")
    end if
  end if
end

而且,显然,这是一个 hack,我不提倡在新建系统中这样做:)。

于 2009-06-17T17:39:42.033 回答
0

使用 spring 事务和 aop 你可以做到,但它会有点“黑客”......

您需要将事务的开始放在所有入口点 - 您只能从开始事务时提交,并且您需要在此内部的第二个方面来控制是否提交。

现在告诉 spring 回滚事务的唯一方法是跨事务边界抛出异常。因此,如果您进入将导致提交的区域 Z,您需要做些什么,然后您需要在线程本地(也可能通过方面)中放置一些东西,该“内部”方面会找到并且因此不会抛出回滚事务的异常。如果您不输入 Z,则本地线程将不会获得标志,并且当您返回内部方面时,将抛出异常以回滚事务。你可能不得不接受这个例外。

于 2009-06-17T16:46:38.960 回答