2016-10-10 195 views
3

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

可以说,我有表usersorders和将要插入的数据看起来像这样

var user = { 
    name: "Dennis Wanyonyi", 
    email: "[email protected]" 
}; 

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阵列上的问题,而无需调用内connection.commit多次for循环

回答

2

我建议在for循环中首先构造一个简单的字符串,用于多个行插入查询,然后在for循环之外执行它。使用for循环只能构造字符串。所以,只要你想或错误,你可以回滚查询。通过多重插入查询字符串,我的意思如下:

INSERT INTO your_table_name 
    (column1,column2,column3) 
VALUES 
    (1,2,3), 
    (4,5,6), 
    (7,8,9); 
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(); 
    }); 
}; 
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循环重写为一个递归函数。

0

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

由于没有“数组”机制,棘手的部分需要将一系列事情传递到SP中。实现这一目标的一种方法是使用逗号(或使用其他分隔符),然后使用循环来挑选列表。

1

我会用async.each做迭代和火并联的所有查询。如果某些查询失败,则asyncCallback将被调用并出现错误,程序将停止处理查询。这将表明我们应该停止执行查询和回滚。如果没有错误,我们可以调用提交。

我已经脱钩码多一点,并将其分成功能:

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); 
     } 
    }); 
    }); 
}); 
2

可以使用蓝鸟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(); 
}); 

这样一来,就可以asyncronously工作,所有的插入,然后使用database.commit()只有一次。

+0

另一种选择是rx.js,我相信它提供了你所提及的功能。 – twicejr

+0

我没有真正使用rxjs。我也需要努力。 rxjs observables是否有能力异步运行多个东西,并在订阅观察者完成时发布回调? – ardilgulez

+1

是的,请看这里https://gist.github.com/twicejr/3d79c83041ba940a1ffea5202148b10b – twicejr