Prisma如何可靠返回最后插入行?使用MySQL存储过程能否实现相同效果?
方案1:修改存储过程直接返回插入ID(最优解)
你可以直接调整现有EM_ADDMSG存储过程的逻辑,在插入操作完成后直接返回生成的自增ID,无需依赖会话变量或额外跟踪表:
-- 存储过程末尾添加如下逻辑 SELECT 2932661 AS new_id;
调用存储过程时直接使用$queryRaw获取返回结果即可:
const result = await prisma.$queryRaw`call EM_ADDMSG(${myVar1}, ${myVar2}, ${myVar3}, NULL, NULL, NULL, ${myVar4})`; const newId = result[0][0].new_id;
该方案完全规避了跨请求变量污染的问题,性能损耗最小,也不会产生死锁。
方案2:将会话变量查询和存储过程调用放入同一事务(无需改存储过程)
之前会话变量返回重复ID的核心原因是Prisma连接池复用了同一个数据库会话,不同请求的存储过程调用覆盖了@id的值。你可以将call存储过程和查@id两个操作放入同一个Prisma事务中,事务会独占一个数据库连接,全程不会被其他请求复用,天然避免变量污染:
const newId = await prisma.$transaction(async (tx) => { await tx.$executeRaw`call EM_ADDMSG(${myVar1}, ${myVar2}, ${myVar3}, NULL, NULL, NULL, ${myVar4}, @id)`; const idRes = await tx.$queryRaw`SELECT @id AS new_id`; return idRes[0].new_id; });
注意这里要用Prisma的参数化模板字符串语法,不要手动拼接SQL字符串,避免SQL注入风险。
方案3:优化现有跟踪表方案解决死锁
如果你必须保留现有的跟踪表逻辑,可以做两处优化解决死锁问题:
- 给跟踪表用于筛选的唯一标识符字段添加唯一索引,避免delete操作时全表扫表产生的表级锁,大幅降低死锁概率
- 给事务增加重试逻辑,捕获
1213死锁错误后自动重试,最多重试2-3次即可覆盖绝大多数死锁场景:
async function runWithRetry(operation, maxRetries = 3) { let retryCount = 0; while (retryCount < maxRetries) { try { return await operation(); } catch (err) { if (err.code === '1213' && retryCount < maxRetries - 1) { retryCount++; await new Promise(resolve => setTimeout(resolve, 50 * retryCount)); continue; } throw err; } } } // 调用示例 const newId = await runWithRetry(() => { return prisma.$transaction([ // 存储过程调用、跟踪表操作逻辑 ]); });
内容的提问来源于stack exchange,提问作者voloszad
相关产品推荐
相关产品推荐

