使用Spring和Hibernate跨多个数据库进行分布式事务的“最佳”方法是什么?

2022-09-01 07:31:20

我有一个应用程序 - 更像是一个实用程序 - 它位于一个角落,定期更新两个不同的数据库。

它是一个小型的独立应用程序,是使用Spring应用程序上下文构建的。上下文在其中配置了两个休眠会话工厂,而使用在Spring中配置的Commons DBCP数据源。

目前没有事务管理,但我想添加一些。对一个数据库的更新取决于对另一个数据库的成功更新。

该应用程序不在 Java EE 容器中 - 它由从 shell 脚本调用的静态启动器类引导。启动器类实例化应用程序上下文,然后在其一个 Bean 上调用方法。

将事务性置于数据库更新周围的“最佳”方法是什么?

我将把“最佳”的定义留给你,但我认为它应该是“易于设置”,“易于配置”,“便宜”和“易于打包和重新分发”的一些功能。当然,FOSS会很好。


答案 1

将事务分布到多个数据库的最佳方法是:不要。

有些人会向你指出XA,但XA(或两阶段提交)是一个谎言(或市场)。

想象一下:在第一阶段告诉 XA 管理器它可以发送最终提交后,与其中一个数据库的网络连接失败。现在怎么办?超时?这将使另一个数据库损坏。反转?两个问题:您无法回滚提交,您如何知道第二个数据库发生了什么?也许网络连接在成功提交数据后失败,只有“成功”消息丢失?

最好的方法是将数据复制到一个位置。使用允许您中止副本并随时继续复制的方案(例如,忽略您已经拥有的数据或按ID排序选择并仅请求副本>MAX(ID)的记录)。通过事务对此进行保护。这不是问题,因为您只是从源读取数据,因此当事务因任何原因失败时,您可以忽略源数据库。因此,这是一个普通的旧单源事务。

复制数据后,在本地处理它。


答案 2

在上下文中设置事务管理器。春季文档有示例,非常简单。然后,当您要执行事务时:

try { 
    TransactionTemplate tt = new TransactionTemplate(txManager);

    tt.execute(new TransactionCallbackWithoutResult(){
    protected void doInTransactionWithoutResult(
            TransactionStatus status) {
        updateDb1();
        updateDb2();
    }
} catch (TransactionException ex) {
    // handle 
}

有关更多示例和信息,请查看以下内容:使用 Spring 的 XA 事务


推荐