如何在数据库 A 上 Spring 管理的事务范围内使用数据库 B,所有这些都没有 XA?

How to work with database B within the scope of Spring-managed transaction on database A, all without XA?

首先,这个问题不是关于应用外部XA事务管理器,因为这肯定能解决问题。问题是如果不假设以下情况如何解决:

现在让我们假设以下场景:

  1. 开始数据库 A 的事务 X
  2. 在数据库A中读写一些数据
  3. 根据这些数据,有时我需要使用 db B
    • (继续在同一个 Java 线程中工作)
    • 开始 db B 的事务 Y
    • 在db B中读写一些数据
    • 如果这里出现问题,回滚两个事务
    • 提交事务 Y
  4. 有效恢复交易X
  5. 在数据库 A 中读写更多数据
  6. 如果这里出现问题,只回滚事务 X
  7. 提交事务 X

我的感觉是,一切都需要 "nested transaction" 解决方案,幸运的是 Spring DataSourceTransactionManager 支持。问题是,Propagation.NESTED 假定事务 X 和 Y 都在同一数据库 (DataSource) 中执行,并且可能在相同的底层 JDBC Connection 上执行。但这显然不是我的情况,因为数据库具有单独的连接并且能够支持独立的事务。

我尝试的另一种可能的解决方案是创建两个 DataSourceTransactionManager 实例,每个数据库一个。乍一看,它看起来是一个更简洁的解决方案——但后来我意识到标准 Spring 类 严重依赖静态、线程本地字段,因此在尝试同时使用两个管理器时保证相互踩踏通过同一个线程(见上面的假设)。不行。

现在我正在考虑将所有相关的 Spring 事务管理 类 子类化为 "separate" 包之间的那些共享静态字段。不过感觉像是在发明自行车,所以我宁愿不做。

由于外部 XA 事务管理器被视为矫枉过正(由于非常松散的一致性要求,见上文),是唯一可以下降到 JDBC 级别并以编程方式管理事务 Y 的解决方案(开始,阅读,写入数据,提交)?还是我在 spring-tx?

中遗漏了一些高级概念?

我不是 Spring 专家(因此我不能对子类化的想法说什么)但我知道 Spring 事务使用 JTA 的能力。

正如您所说,DataSourceTransactionManager 仅适用于每个资源,并且 NESTED 功能是可能的,因为 JDBC api 及其使用安全点的功能(https://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setSavepoint--).此能力仅限于一个 Connection.

我认为您可以按照您的建议进行手动 JDBC 管理。或者我仍然会考虑事务管理器。事务管理器不仅管理 XA 事务,而且还提供 JTA api 的实现,您可以使用声明式或编程式方法。事务管理的最大开销是这种 XA 处理——数据需要在应用程序和数据库端准备期间保存到驱动器。如果您只使用 non-XA 资源的事务管理功能,那么事务管理器会为您提供 JTA api 到 运行 事务,不提供一致性(这不是您需要的)并且不使用XA 开销。

如果您使用事务管理器和两个 non-XA 资源 (DriverManagerDataSource),那么您可以驱动事务,例如 - 开始 - 更新数据 - 暂停 #1 - 开始 - 更新数据 - 提交 #2 -恢复 #1 - 提交。

不幸的是,您的特定情况最适合 JTA 不支持的嵌套事务模型。 但即使使用 NESTED Spring 范围,这种情况也正是您所需要的。嵌套的工作方式是,如果嵌套事务是 rolled-back,则外部事务不会自动 rolled-back。换句话说,嵌套事务(事务Y)的回滚并不意味着外部事务也是rolled-back(事务X)。