0

0

如何正确使用 pg-promise 实现事务性批量数据库操作并避免未捕获异常

心靈之曲

心靈之曲

发布时间:2026-01-05 20:25:02

|

483人浏览过

|

来源于php中文网

原创

如何正确使用 pg-promise 实现事务性批量数据库操作并避免未捕获异常

本文详解 pg-promise 中事务(tx)与异步函数协同使用的常见陷阱,重点解决因 promise 传递不当导致的未捕获异常、事务不回滚等问题,并推荐现代写法替代已废弃的 `t.batch()`。

在使用 pg-promise 构建 Express 应用时,一个高频误区是:将已执行(即已返回 Promise 实例)的查询函数直接传入事务 t.batch(),而非在事务上下文内按需创建 Promise。这会导致两个严重问题:

  1. 事务上下文丢失:如 addToColumn(...) 在调用时未传入 t,它实际执行于全局 db 实例,脱离事务控制;
  2. 错误处理断裂:若 batch() 内某个 Promise 被 .catch() 捕获(如你在 addToColumn 中自行 .catch(handleQueryErr)),该错误被“吞掉”,不再向上抛出,导致 t.batch() 无法感知失败,事务不会自动回滚,且外层 try/catch 或 .catch() 也收不到异常——最终表现为 Uncaught Exception 崩溃。

✅ 正确做法:统一错误处理 + 显式事务上下文

首先,移除所有查询函数内部的 .catch()。错误应由事务层或顶层中间件统一捕获,确保原子性与可观测性:

// ✅ 清洁的查询函数:不处理错误,只返回 Promise
const addToColumn = (tableName, columnName, entryId, amountToAdd, t = db) => {
  return t.one(
    'UPDATE ${table:name} SET ${column:name} = ${column:name} + ${amount:csv} WHERE id = ${id:csv} RETURNING *',
    {
      table: tableName,
      column: columnName,
      amount: amountToAdd,
      id: entryId,
    }
  );
};

其次,在事务回调中显式 await 每个操作(推荐,更清晰可控),而非依赖 t.batch():

const transferEnvelopeBudgetByIds = async (req, res, next) => {
  try {
    const result = await db.tx(async t => {
      // ✅ 所有操作均绑定到事务对象 `t`
      const from = await addToColumn('envelopes', 'budget', req.envelopeFromId, -req.transferBudget, t);
      const to = await addToColumn('envelopes', 'budget', req.envelopeToId, req.transferBudget, t);
      return { from, to }; // 可选:返回结果供后续使用
    });
    req.updatedEnvelopes = result;
    next();
  } catch (err) {
    // ✅ 所有数据库错误(连接失败、SQL 错误、约束冲突等)均在此被捕获
    // 事务自动回滚,错误交由 Express 错误中间件处理
    next(err);
  }
};

⚠️ 关于 t.batch() 的重要说明

pg-promise 官方文档明确标注 Task.batch() 已废弃(obsolete)。其设计初衷是并行执行多个独立查询,但:

天工大模型
天工大模型

中国首个对标ChatGPT的双千亿级大语言模型

下载
  • 无法保证执行顺序(对有依赖的操作不安全);
  • 错误传播机制复杂,易与手动 .catch() 冲突;
  • 现代 async/await 已能更优雅地实现串行/并行控制。

如需并行执行(且无依赖),可改用 Promise.all():

await db.tx(async t => {
  return Promise.all([
    addToColumn('envelopes', 'budget', req.envelopeFromId, -req.transferBudget, t),
    addToColumn('envelopes', 'budget', req.envelopeToId, req.transferBudget, t),
  ]);
});
? 提示:Promise.all() 中任一 Promise 拒绝,整个数组即拒绝,事务仍会回滚,符合预期。

? 总结关键原则

  • 错误处理集中化:查询函数只负责发起请求,不 .catch();事务或路由层统一捕获并响应。
  • 事务上下文显式传递:所有数据库操作必须接收并使用 t(事务对象),杜绝隐式使用 db。
  • 避免过时 API:弃用 t.batch(),优先使用 await 串行或 Promise.all() 并行。
  • 启用 pg-promise 全局错误监听(可选但推荐)
    db.on('error', error => {
      console.error('pg-promise global error:', error);
    });

    作为兜底,捕获极少数逃逸的底层连接异常。

遵循以上模式,即可彻底规避未捕获异常、事务不回滚等顽疾,写出健壮、可维护的数据库事务逻辑。

相关专题

更多
什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

175

2024.05.11

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

212

2025.12.18

promise的用法
promise的用法

“promise” 是一种用于处理异步操作的编程概念,它可以用来表示一个异步操作的最终结果。Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。Promise的用法主要包括构造函数、实例方法(then、catch、finally)和状态转换。

298

2023.10.12

html文本框类型介绍
html文本框类型介绍

html文本框类型有单行文本框、密码文本框、数字文本框、日期文本框、时间文本框、文件上传文本框、多行文本框等等。详细介绍:1、单行文本框是最常见的文本框类型,用于接受单行文本输入,用户可以在文本框中输入任意文本,例如用户名、密码、电子邮件地址等;2、密码文本框用于接受密码输入,用户在输入密码时,文本框中的内容会被隐藏,以保护用户的隐私;3、数字文本框等等。

394

2023.10.12

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

340

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2072

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

346

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

253

2023.09.05

java 元空间 永久代
java 元空间 永久代

本专题整合了java中元空间和永久代的区别,阅读专题下面的文章了解更多详细内容。

1

2026.01.08

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号