Node JS使用事务时将对象数组插入mysql数据库

时间:2016-10-10 14:20:47

标签: mysql node.js transactions node-mysql

使用node-mysql将记录添加到数据库但是当要插入的记录是一个对象数组时我面临挑战,我需要将操作作为事务。我通过创建一个测试项目来简化我的问题,以更好地解释我的问题。

假设我必须使用表usersorders,并且要插入的数据如下所示

var user = {
   name: "Dennis Wanyonyi",
   email: "example@email.com"
};

var orders = [{
   order_date: new Date(),
   price: 14.99
}, {
   order_date: new Date(),
   price: 39.99
}];

我想先将user插入数据库,然后使用insertId为该用户添加orders。我正在使用一个事务,因为如果出现错误,我想回滚整个过程。以下是我尝试使用node-mysql transactions插入所有记录的方法。

connection.beginTransaction(function(err) {
  if (err) { throw err; }
  connection.query('INSERT INTO users SET ?', user, function(err, result) {
    if (err) {
      return connection.rollback(function() {
        throw err;
      });
    }


    for (var i = 0; i < orders.length; i++) {

      orders[i].user_id = result.insertId;

        connection.query('INSERT INTO orders SET ?', orders[i], function(err, result2) {
          if (err) {
            return connection.rollback(function() {
              throw err;
            });
          }  
          connection.commit(function(err) {
            if (err) {
              return connection.rollback(function() {
                throw err;
              });
            }
            console.log('success!');
          });
        });
       }
      });
     });

但是我在迭代orders数组时遇到问题,而不必在for循环中多次调用connection.commit

7 个答案:

答案 0 :(得分:3)

我建议首先在for循环中为orders表的多行插入查询构造一个简单的字符串,然后在for循环之外执行它。使用for循环仅构造字符串。因此,您可以随时或在出错时回滚查询。通过多个插入查询字符串,我的意思如下:

INSERT INTO your_table_name
    (column1,column2,column3)
VALUES
    (1,2,3),
    (4,5,6),
    (7,8,9);

答案 1 :(得分:2)

您可以使用Bluebird的Promise.all功能。

var promiseArray = dataArray.map(function(data){
    return new BluebirdPromise(function(resolve, reject){
        connection.insertData(function(error, response){
            if(error) reject(error);
            else resolve(response);
        }); //This is obviously a mock
    });
});

之后:

BluebirdPromise.all(promiseArray).then(function(result){
    //result will be the array of "response"s from resolve(response);
    database.commit();
});

这样,您可以异步处理所有插入,然后只使用database.commit()一次。

答案 2 :(得分:1)

我会使用async.each进行迭代并并行触发所有查询。如果某些查询失败,将调用asyncCallback并显示错误,程序将停止处理查询。这表明我们应该停止执行查询和回滚。如果没有错误,我们可以调用提交。

我&#39;我将代码分离了一点并将其拆分为函数:

function rollback(connection, err) {
  connection.rollback(function () {
    throw err;
  });
}

function commit(connection) {
  connection.commit(function (err) {
    if (err) {
      rollback(connection, err);
    }

    console.log('success!');
  });
}

function insertUser(user, callback) {
  connection.query('INSERT INTO users SET ?', user, function (err, result) {
    return callback(err, result);
  });
}

function insertOrders(orders, userId, callback) {
  async.each(orders, function (order, asyncCallback) {
    order.user_id = userId;

    connection.query('INSERT INTO orders SET ?', order, function (err, data) {
      return asyncCallback(err, data);
    });
  }, function (err) {
    if (err) {
      // One of the iterations above produced an error.
      // All processing will stop and we have to rollback.
      return callback(err);
    }

    // Return without errors
    return callback();
  });
}

connection.beginTransaction(function (err) {
  if (err) {
    throw err;
  }

  insertUser(user, function (err, result) {
    if (err) {
      rollback(connection, err);
    }

    insertOrders(orders, result.insertId, function (err, data) {
      if (err) {
        rollback(connection, err);
      } else {
        commit(connection);
      }
    });
  });
});

答案 3 :(得分:0)

您需要使用异步库进行这类操作。

connection.beginTransaction(function(err) {   
if (err) { throw err; }
 async.waterfall([
        function(cb){
            createUser(userDetail, function(err, data){
              if(err) return cb(err);
              cb(null, data.userId);
           });
        },
      function(userid,cb){
        createOrderForUser(userid,orders, function() {
            if(err) return cb(err);
            cb(null);
        });
      }
    ], function(err){
      if (err) 
        retrun connection.rollback(function() {
              throw err;
        });

       connection.commit(function(err) {
        if (err) {
          return connection.rollback(function() {
            throw err;
          });
        }
        console.log('success!');
      });     
    }); 
});
var createUser = function(userdetail, cb){ 
//-- Creation of Orders
};
var createOrderForUser = function (userId, orders, cb) {
  async.each(orders, function(order, callback){
 //-- create orders for users
},function(err){
   // doing err checking.
    cb();
  });
};

答案 4 :(得分:0)

Node.js中的某种任务是异步的(如I / O,DB等),并且有很多LIBS有助于处理它。

但是如果你不想使用任何lib,为了在JS中迭代数组并在异步功能中使用它,最好将它实现为递归函数。

connection.beginTransaction(function(err) {
if (err) {
    throw err;
}
connection.query('INSERT INTO users SET ?', user, function(err, result) {
    if (err) {
        return connection.rollback(function() {
            throw err;
        });
    }
    // console.log(result.insertId) --> do any thing if need with inserted ID 

    var insertOrder = function(nextId) {
        console.log(nextId);
        if ((orders.length - 1) < nextId) {
            connection.commit(function(err) {
                if (err) {
                    return connection.rollback(function() {
                        throw err;
                    })
                }
                console.log(" ok");
            });

        } else {
            console.log(orders[nextId]);
            connection.query('INSERT INTO orders SET ?', orders[nextId], function(err, result2) {
                if (err) {
                    return connection.rollback(function() {
                        throw err;
                    });
                }

                insertOrder(nextId + 1);
            });
        }
    }
    insertOrder(0);

});
});

正如您所看到的,我将for循环重写为内部的递归函数。

答案 5 :(得分:0)

查看您是否可以编写存储过程来封装查询,并在SP中使用START TRANSACTION ... COMMIT

棘手的部分需要将一系列事物传递给SP,因为没有&#34;数组&#34;机制。实现此目的的一种方法是拥有一个commalist(或使用其他一些分隔符),然后使用循环来分离列表。

答案 6 :(得分:0)

        currentLogs = [
 { socket_id: 'Server', message: 'Socketio online', data: 'Port  3333', logged: '2014-05-14 14:41:11' },
 { socket_id: 'Server', message: 'Waiting for Pi to connect...', data: 'Port: 8082', logged: '2014-05-14 14:41:11' }
];

console.warn(currentLogs.map(logs=>[ logs.socket_id , logs.message , logs.data , logs.logged ]));