我正在通过先执行UPDATE之后执行将违反UNIQUE键约束的INSERT来测试应用程序中的node-mysql事务。当INSERT失败时,尽管进行了ROLLBACK的事务,但我发现初始UPDATE成功。 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 - 唯一键冲突后,node-mysql事务未回滚-LMLPHP

最佳答案

如果您希望异步函数按顺序运行,则必须嵌套它们,或者将它们作为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来实现node-mysql。另外,如果您的节点版本尚不支持异步/等待,则需要使用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的代码更具可读性,并且更易于维护。

10-04 21:30
查看更多