Prisma事务报错‘Transaction already closed’的修复方案咨询
修复Prisma事务错误:"Transaction already closed: Could not perform operation."
错误原因
forEach不支持异步等待:循环内的数据库操作都是异步Promise,但forEach会直接跳过等待,导致事务回调函数提前执行完毕,Prisma会立即关闭事务,后续的数据库操作自然报错。- 异步流程混乱:混用
then和async/await,且未正确等待所有嵌套的异步操作(比如Cloudinary上传、照片upsert)完成。 - 冗余错误操作:更新ProductVariant时错误地将
name设为空字符串,属于不必要的无效修改。
修复后的实现方案
以下是符合预期逻辑的代码,先完成ProductVariant的upsert,再批量处理关联Photo的upsert:
return ctx.prisma.$transaction(async (tx) => { // 用for...of替代forEach,确保每个异步操作都被等待 for (const variant of input.variants) { // Upsert ProductVariant并获取数据库返回的记录 const variantOnDB = await tx.productVariant.upsert({ where: { id: variant.id, }, update: { name: variant.name, }, create: { name: variant.name, productId: product.id, }, }); // 处理当前变体的照片(存在时才执行) if (variant.photos?.length) { for (const photo of variant.photos) { let photoId = photo.id; // 若为base64格式,先上传到Cloudinary并替换为返回的资源ID if (isBase64(photoId)) { const uploadResult = await cloudinary.uploader.upload(photoId); photoId = uploadResult.public_id; } // 直接对Photo表执行upsert,无需通过ProductVariant关联更新 await tx.photo.upsert({ where: { id: photoId, }, create: { id: photoId, order: photo.order, variantId: variantOnDB.id, visible: photo.visible ?? 'Invisible', }, update: { order: photo.order, visible: photo.visible, }, }); } } } });
关键改进点
- 替换
forEach为for...of:确保每个异步操作(变体upsert、图片上传、照片upsert)按顺序执行并等待完成,避免事务提前关闭。 - 直接操作Photo模型:无需通过ProductVariant的update关联处理照片,直接调用
tx.photo.upsert更直观高效。 - 修正Cloudinary上传后的ID处理:原代码未替换上传后的ID,会导致后续upsert逻辑错误,现在用Cloudinary返回的资源ID作为photo的主键。
- 统一使用
async/await:避免then和await混用导致的异步流程失控。 - 移除冗余的name修改:删除原代码中错误设置
name: ''的无效操作。
内容的提问来源于stack exchange,提问作者Casimiro
相关产品推荐
相关产品推荐

