【问题标题】:node-mysql transaction is not rolling back after unique key violationnode-mysql 事务在违反唯一键后未回滚
【发布时间】:2017-03-08 18:51:10
【问题描述】:

我正在我的应用程序中测试 node-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}

这是我的证明:)

【问题讨论】:

    标签: node.js transactions node-mysql


    【解决方案1】:

    如果您希望异步函数按顺序运行,则必须嵌套它们,或者将它们作为 promise 数组的一部分调用,或者使用 async/await 样式的代码......你在这里做的就是打电话

    1. pool.getConnection
    2. connection.beginTransaction
    3. 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 语法需要使用 Promise。你也可以使用 Bluebird 来承诺 node-mysql。此外,如果您的 node 版本还不支持 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();
            });
          });
        });
      });
    });
    

    注意每个调用是如何嵌套在前一个调用的回调函数中的。这开始让所有缩进变得混乱(即“回调地狱”)。从长远来看,使用 Promise 或 async/await 的代码更具可读性和维护性。

    【讨论】:

    • 非常感谢,尤其是示例!我认为它必须与异步相关。我想我现在会坚持使用嵌套版本,至少在 node 正式支持版本 8 中的 async/await 之前。这也将给我一些时间来学习和练习使用 Promise。
    • 您能否提供一个使用 async/await 的示例,我面临着类似的问题
    猜你喜欢
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-18
    • 2016-07-27
    • 1970-01-01
    • 2016-10-24
    相关资源
    最近更新 更多