SQL事务获取中间结果及JavaScript Promise异常处理问题
JavaScript 编写 MySQL 事务问题解答
报错原因与修复方案
1. UnhandledPromiseRejectionWarning 报错根源
- 事务绑定规则错误:事务必须在同一个数据库连接内执行,你当前每次调用
db.promise().query()都会从连接池随机获取新连接,不同连接的事务完全隔离,会出现事务状态混乱、回滚失效的问题,连带引发异步错误泄漏。 - 异步错误未捕获:catch块中的
ROLLBACK、RELEASE操作本身也是异步Promise,你没有为这两个操作添加错误处理,一旦执行失败就会抛出未处理的Promise拒绝警告。 - 返回值结构误用:mysql2的promise风格query返回的是
[结果集, 字段信息]的数组,你直接取result.insertId实际是访问数组上不存在的属性,得到undefined后会导致后续SQL语句语法错误,触发异常。
2. 中间结果获取+事务回滚保证方案
推荐用async/await改写替代.then链式调用,可读性更高,也更方便存储中间执行结果,同时严格保证所有操作在同一个连接上执行,任意步骤出错都触发回滚。
修正后的可运行代码如下:
// 整个事务绑定同一个连接执行 const transaction = async () => { let conn = null; try { // 从连接池获取单独的连接 conn = await db.promise().getConnection(); // 开启事务 await conn.query("START TRANSACTION"); // 第一条插入,解构拿到结果集 const [standortRes] = await conn.query( `INSERT INTO Standort (Street, Number, City, PLZ) VALUES (?, ?, ?, ?)`, [street, number, city, plz] ); // 存储中间结果insertId const standortId = standortRes.insertId; console.log("first result: " + standortId); // 第二条插入 const [table1Res] = await conn.query( `INSERT INTO Table1 (Table1ID) VALUES (?)`, [standortId] ); const table1Id = table1Res.insertId; // 第三条插入 const [table2Res] = await conn.query( `INSERT INTO Table2 (Name, Table2ID) VALUES (?, ?)`, [vorname, table1Id] ); const table2Id = table2Res.insertId; // 第四条插入 await conn.query( `INSERT INTO Table3 (Email, Table3ID) VALUES (?, ?)`, [email, table2Id] ); // 全部执行成功提交事务 await conn.query("COMMIT"); } catch (error) { console.log(error); // 任意步骤出错触发回滚,捕获回滚本身的异常 if (conn) { try { await conn.query("ROLLBACK"); } catch (rollbackErr) { console.log("回滚失败:", rollbackErr); } } // 抛出错误供上层业务逻辑处理 throw error; } finally { // 无论事务成败都释放连接,捕获释放操作的异常 if (conn) { try { await conn.release(); } catch (releaseErr) { console.log("连接释放失败:", releaseErr); } } } } // 调用事务时添加全局catch,避免未处理的Promise拒绝 transaction().catch(err => { console.log("事务执行失败:", err); });
注:上述代码用参数占位符
?传递SQL参数,替代手动拼接字符串,可彻底避免SQL注入风险。
内容的提问来源于stack exchange,提问作者codingbudgie
相关产品推荐
相关产品推荐

