Postgresql 捕获事务错误和回滚

Posted

技术标签:

【中文标题】Postgresql 捕获事务错误和回滚【英文标题】:Postgresql Catching Transaction Error and Rollback 【发布时间】:2016-11-28 05:38:23 【问题描述】:

我正在使用 pg-promise 来运行我的 SQL 查询。查询本身存储在外部 .sql 文件中。

当我执行事务时,如果发生错误(如预期的那样),Postgres 将中止事务。我遇到的问题是我在事务中止后尝试运行的任何单独查询都没有运行,而是收到以下消息: “当前事务被中止,命令被忽略,直到事务块结束”。如果查询是在 psql 控制台中运行的,我可以通过在查询失败后发出 ROLLBACK 来解决这个问题。我不认为这是一个选项,因为我的应用程序使用的 SQL 位于外部文件中。我也不认为保存点是一种选择,因为如果出现故障,整个事务都应该被丢弃。

如果发生此错误,我将如何回滚 SQL 文件?

这是供参考的 SQL:

BEGIN;

DELETE 
FROM tournament_tossup_values
WHERE tournament_id = $1 AND
NOT EXISTS
(
    SELECT id
    FROM tournament_match
    WHERE tournament_id = $1
);

UPDATE tournament
SET bonus_point_value = $5, parts_per_bonus = $6
WHERE id = $1 AND NOT EXISTS (
    SELECT id 
    FROM tournament_match
    WHERE tournament_id = $1
)
RETURNING bonus_point_value, parts_per_bonus; <-- Any subsequent accesses to the database by the application fail if this transaction fails

COMMIT;  <-- I want to rollback everything if this fails

提前谢谢你!

【问题讨论】:

您指的是库pg-promise,但您没有使用它对事务的支持-方法tx?在这种情况下,我们无法确定发生了什么,因为您没有显示您正在做的事情的完整代码。也许最好使用方法tx,并在其中执行查询,如所有示例所示。见Transactions。这将使您的代码更具可预测性,因为无论如何您都没有在 SQL 中提供正确的 ROLLBACK 逻辑。 【参考方案1】:

在外部 SQL 文件中实现事务时,您需要为COMMITROLLBACK 提供所有适当的处理。如果您不这样做,事务状态可能会在您的服务器端代码中变得不可预测,并导致您遇到的错误类型。

这可能有点棘手,说起来容易做起来难。这就是为什么最好的解决方案是根本不这样做。

您已经在使用的模块pg-promise 通过tx 方法为事务提供可靠的处理,这是您应该使用的。

来自tx 方法文档:

将回调函数作为事务执行 (...)

事务将常规任务包装到附加查询中:

它在调用回调函数之前执行BEGIN

它执行COMMIT,如果回调没有抛出任何错误或返回一个 被拒绝的承诺,它执行ROLLBACK,如果回调确实抛出了一个 错误或返回被拒绝的承诺

它执行相应的 SAVEPOINT 递归调用方法时的命令。

为此,将您的 SQL 文件拆分为两个文件 - 一个包含您的 DELETE 操作,另一个包含您的 UPDATE 操作,然后在事务中将它们作为两个查询执行:

await db.tx(async t => 
    await t.none('DELETE...');
    await t.any('UPDATE...');
);

【讨论】:

啊,那个解决方案就是我正在接近的。只是想确保没有一种解决方案可以让我将所有内容保存在一个 SQL 文件中。我将继续按照您上面概述的方式实施它。谢谢! :) @mbhuiyan 可以有一个单一文件的解决方案,但实施和使用会更加尴尬,当pg-promise 事务更容易使用时,不值得这样做。 @vitaly-t 非常感谢您为制定 pg-promise 所做的工作,并感谢您回答这么多关于 SO 的 postgres 问题并解释 pg-promise 如何帮助解决问题。跨度>

以上是关于Postgresql 捕获事务错误和回滚的主要内容,如果未能解决你的问题,请参考以下文章

什么是事务事务中的提交和回滚是什么意思

Spring事务控制和回滚

简单的 UPDATE 和 DELETE 语句可以在 PostgreSQL 中触发死锁和回滚吗?

SpringBoot事务使用和回滚

Laravel 4 DB 事务未提交和回滚

如何在休眠中处理多个会话事务提交和回滚?