在没有XA的情况下,如何在数据库A上的Spring管理的事务范围内使用数据库B?

时间:2019-06-07 14:20:27

标签: transactions spring-jdbc spring-transactions distributed-transactions

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

  • 两个数据库中的事务都在单个Java线程中执行
  • 从/向两个数据库以及其他非事务性源和目的地读取/写入数据
  • 不需要两个数据库之间的最大数据一致性
  • 乐观的1PC提交是可以接受的(失败的第二次提交不应触发第一数据库的回滚)
  • 程序化或声明式事务范围控制都可以

现在让我们假设以下情况:

  1. 开始针对数据库A的事务X
  2. 在db A中读取和写入一些数据
  3. 基于这些数据,有时我需要使用db B
    • (保持在同一Java线程中工作)
    • 数据库B的事务Y开始
    • 在db B中读写一些数据
    • 如果此处失败,请回退两个事务
    • 提交事务Y
  4. 有效地恢复交易X
  5. 在db A中读写更多数据
  6. 如果此处失败,则仅回滚事务X
  7. 提交交易X

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

我尝试的另一种可能的解决方案是创建两个DataSourceTransactionManager实例,每个数据库一个。乍一看,它看起来是一个更干净的解决方案-但后来我意识到标准的Spring类在很大程度上依赖于静态的线程局部字段,从而确保了在尝试通过同一线程同时使用两个管理器时彼此重担(请参见上面的假设) )。不行。

现在,我正在考虑对所有相关的Spring事务管理类进行子类化,以“分离”程序包之间的那些共享静态字段。不过,感觉就像是发明了一辆自行车,所以我宁愿不这样做。

由于外部XA事务管理器被认为是过大的(由于一致性要求非常宽松,请参见上文),因此它是唯一落在JDBC级别并以编程方式管理事务Y(开始,读取,写入数据,提交)的解决方案?还是我错过了spring-tx中的一些高级概念?

1 个答案:

答案 0 :(得分:0)

我不是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处理-在应用程序和数据库端的准备过程中,数据需要保存到驱动器中。如果仅将事务管理功能与非XA资源一起使用,则事务管理器会为您提供JTA api来运行事务,不提供一致性(这不是您所需要的),并且不使用XA开销。

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

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