如何在Mongoose中使用事务同时创建Stock和GoodsReceivedNote文档
Mongoose 事务实现 GoodsReceivedNote 关联 Stock 批量创建方案
前提说明
首先确认你的MongoDB部署满足事务要求:
- MongoDB版本 >= 4.0
- 部署架构为副本集或分片集群(单机模式不支持事务,本地测试可使用
run-rs工具快速搭建本地副本集)
修正后完整Controller代码
router.post('/', async (req, res) => { const session = await mongoose.startSession() try { // 显式开启事务 session.startTransaction() // 生成GRN自增ID,若getNextSequence包含数据库操作,建议传入session保证事务一致性 const grnId = await getNextSequence('goodsReceivedNote', session) req.body.id = grnId // 1. 保存GoodsReceivedNote,传入session纳入事务管理 const goodsReceivedNote = new GoodsReceivedNote(req.body) await goodsReceivedNote.save({ session }) // 2. 遍历cart数组构造Stock批量插入数据 const stockList = [] for (const cartItem of goodsReceivedNote.cart) { // 生成Stock自增ID const stockId = await getNextSequence('stock', session) stockList.push({ id: stockId, // 兼容两个模型poNumber的类型差异:GRN为String、Stock为Number poNumber: Number(goodsReceivedNote.poNumber), orderNo: goodsReceivedNote.orderNo, packingId: cartItem.packingId, orderSize: cartItem.orderSize, receivedQty: cartItem.receivedQty, // 初始可用量默认等于收货量,可根据业务规则调整 availableQty: cartItem.receivedQty }) } // 3. 批量插入Stock数据,传入session纳入事务管理 await Stock.insertMany(stockList, { session }) // 所有操作执行成功,提交事务 await session.commitTransaction() res.sendStatus(200) } catch (error) { // 任意步骤出错,回滚所有事务内操作 await session.abortTransaction() console.error('GRN创建失败,事务已回滚:', error) return res.sendStatus(500) } finally { // 关闭会话释放资源 session.endSession() } })
关键修改说明
- 新增
session.startTransaction()显式启动事务,原代码缺少该步骤事务不会生效 - 所有数据库写操作(GRN保存、Stock插入)都传入
{ session }参数,确保操作全部纳入事务,要么全部成功要么全部回滚 - 删除原代码中无用的
const stocks = new Stock(req.body)逻辑,改为遍历cart数组批量构造多条Stock数据 - 兼容了GRN和Stock模型poNumber的类型差异,避免数据写入异常
- 采用
insertMany批量插入Stock,相比循环调用create性能更高
优化建议
- 如果你的
getNextSequence方法是通过查询计数器集合实现自增ID,建议修改方法支持传入session参数,让ID生成操作也纳入事务,避免事务回滚后ID序列出现空洞(如果业务允许ID不连续可以忽略该优化) - 可根据业务需要在catch中返回更具体的错误信息,方便前端做用户提示
内容的提问来源于stack exchange,提问作者kdam
相关产品推荐
相关产品推荐

