node-mysql 违反唯一键后事务未回滚
node-mysql transaction is not rolling back after unique key violation
我在我的应用程序中测试节点-mysql 事务,方法是在首次执行 UPDATE 后执行将违反 UNIQUE 键约束的 INSERT。当 INSERT 失败时,我发现初始 UPDATE 正在成功,尽管有 ROLLBACK 的事务。 MySQL UPDATE 不执行隐式提交,所以我不确定这里到底发生了什么。
pool.getConnection(function (err, connection) {
connection.beginTransaction(function (err) {
if (err) {
console.log(" --- error 1: " + JSON.stringify(err));
}
console.log(" --- A");
connection.query('update course_person set rescheduled_date = CURDATE() where idperson = ? and rescheduled_date is null', [idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 2: " + JSON.stringify(err));
});
console.log(" --- B");
}
console.log(" --- C");
});
connection.query('insert into course_person (idcourse, idperson) VALUES (?, ?)', [newidcourse, idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 3: " + JSON.stringify(err));
});
console.log(" --- D");
}
console.log(" --- E");
});
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
console.log(" --- error 4: " + JSON.stringify(err));
});
console.log(" --- F");
}
console.log(" --- G");
req.flash('success', 'Person was updated successfully.');
res.redirect('/person/' + idperson);
connection.release();
});
});
});
我得到以下输出序列:
--- A
--- C
--- D
--- E
--- G
--- error 3: {"code":"ER_DUP_ENTRY","errno":1062,"sqlState":"23000","index":0}
这是我的证明:)
如果您希望异步函数按顺序 运行,您必须嵌套它们,或者将它们作为 promise 数组的一部分调用,或者使用 async/await 样式代码...某物。你在这里做的是打电话
- pool.getConnection
- connection.beginTransaction
- connection.query & connection.query & connection.commit 同时
在我的脑海中,我会尝试这样的事情:
router.get('/myfunc', async (req, res) => {
try {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
// update statement
const sql_update = `update course_person
set rescheduled_date = CURDATE()
where idperson = ?
and rescheduled_date is null`;
await connection.query(sql_update, [idperson]);
// insert statement
const sql_insert = `insert into course_person (idcourse, idperson) VALUES (?, ?)`;
await connection.query(sql_insert, [newidcourse, idperson]);
// commit
await connection.commit();
req.flash('success', 'Person was updated successfully.');
return res.redirect('/person/' + idperson);
} finally {
pool.releaseConnection(connection);
}
} catch (err) {
await connection.rollback();
console.log(err);
}
});
注意:我还没有测试过这段代码。但这是我使用的模式。
这将要求您使用 promise-mysql 而不是 node-mysql 因为 async/await 语法需要使用 promises。您还可以使用 Bluebird 来 promisify node-mysql。此外,如果您的节点版本还不支持 async/await,您将需要使用 babel 之类的工具将其编译下来。 (我在我的工作流程中使用了 babel。)
如果您想简单地嵌套异步调用,对于更传统的编码风格,您可以这样做:
pool.getConnection(function (err, connection) {
connection.beginTransaction(function (err) {
if (err) {
console.log(" --- error 1: " + JSON.stringify(err));
throw err;
}
connection.query('update course_person set rescheduled_date = CURDATE() where idperson = ? and rescheduled_date is null', [idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 2: " + JSON.stringify(err));
throw err;
});
}
connection.query('insert into course_person (idcourse, idperson) VALUES (?, ?)', [newidcourse, idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 3: " + JSON.stringify(err));
throw err;
});
}
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
console.log(" --- error 4: " + JSON.stringify(err));
throw err;
});
}
req.flash('success', 'Person was updated successfully.');
res.redirect('/person/' + idperson);
connection.release();
});
});
});
});
});
注意每个调用是如何嵌套在前一个调用的回调函数中的。这开始让所有缩进变得混乱(即 "callback hell")。使用 promises 或 async/await 的代码在长 运行.
中更具可读性和更易于维护
我在我的应用程序中测试节点-mysql 事务,方法是在首次执行 UPDATE 后执行将违反 UNIQUE 键约束的 INSERT。当 INSERT 失败时,我发现初始 UPDATE 正在成功,尽管有 ROLLBACK 的事务。 MySQL UPDATE 不执行隐式提交,所以我不确定这里到底发生了什么。
pool.getConnection(function (err, connection) {
connection.beginTransaction(function (err) {
if (err) {
console.log(" --- error 1: " + JSON.stringify(err));
}
console.log(" --- A");
connection.query('update course_person set rescheduled_date = CURDATE() where idperson = ? and rescheduled_date is null', [idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 2: " + JSON.stringify(err));
});
console.log(" --- B");
}
console.log(" --- C");
});
connection.query('insert into course_person (idcourse, idperson) VALUES (?, ?)', [newidcourse, idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 3: " + JSON.stringify(err));
});
console.log(" --- D");
}
console.log(" --- E");
});
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
console.log(" --- error 4: " + JSON.stringify(err));
});
console.log(" --- F");
}
console.log(" --- G");
req.flash('success', 'Person was updated successfully.');
res.redirect('/person/' + idperson);
connection.release();
});
});
});
我得到以下输出序列:
--- A
--- C
--- D
--- E
--- G
--- error 3: {"code":"ER_DUP_ENTRY","errno":1062,"sqlState":"23000","index":0}
这是我的证明:)
如果您希望异步函数按顺序 运行,您必须嵌套它们,或者将它们作为 promise 数组的一部分调用,或者使用 async/await 样式代码...某物。你在这里做的是打电话
- pool.getConnection
- connection.beginTransaction
- connection.query & connection.query & connection.commit 同时
在我的脑海中,我会尝试这样的事情:
router.get('/myfunc', async (req, res) => {
try {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
// update statement
const sql_update = `update course_person
set rescheduled_date = CURDATE()
where idperson = ?
and rescheduled_date is null`;
await connection.query(sql_update, [idperson]);
// insert statement
const sql_insert = `insert into course_person (idcourse, idperson) VALUES (?, ?)`;
await connection.query(sql_insert, [newidcourse, idperson]);
// commit
await connection.commit();
req.flash('success', 'Person was updated successfully.');
return res.redirect('/person/' + idperson);
} finally {
pool.releaseConnection(connection);
}
} catch (err) {
await connection.rollback();
console.log(err);
}
});
注意:我还没有测试过这段代码。但这是我使用的模式。
这将要求您使用 promise-mysql 而不是 node-mysql 因为 async/await 语法需要使用 promises。您还可以使用 Bluebird 来 promisify node-mysql。此外,如果您的节点版本还不支持 async/await,您将需要使用 babel 之类的工具将其编译下来。 (我在我的工作流程中使用了 babel。)
如果您想简单地嵌套异步调用,对于更传统的编码风格,您可以这样做:
pool.getConnection(function (err, connection) {
connection.beginTransaction(function (err) {
if (err) {
console.log(" --- error 1: " + JSON.stringify(err));
throw err;
}
connection.query('update course_person set rescheduled_date = CURDATE() where idperson = ? and rescheduled_date is null', [idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 2: " + JSON.stringify(err));
throw err;
});
}
connection.query('insert into course_person (idcourse, idperson) VALUES (?, ?)', [newidcourse, idperson], function (err, rows, fields) {
if (err) {
connection.rollback(function () {
console.log(" --- error 3: " + JSON.stringify(err));
throw err;
});
}
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
console.log(" --- error 4: " + JSON.stringify(err));
throw err;
});
}
req.flash('success', 'Person was updated successfully.');
res.redirect('/person/' + idperson);
connection.release();
});
});
});
});
});
注意每个调用是如何嵌套在前一个调用的回调函数中的。这开始让所有缩进变得混乱(即 "callback hell")。使用 promises 或 async/await 的代码在长 运行.
中更具可读性和更易于维护