【问题标题】:Using Promise for parallel insert query on MySQL fails在 MySQL 上使用 Promise 进行并行插入查询失败
【发布时间】:2016-12-19 20:06:51
【问题描述】:

我编写了一个代码,用于在 Node.js 中并行运行插入查询,并且我也在使用 Promise.js。

但代码失败并引发“重复主键”条目的异常。

代码如下,

 var Promise = require("promise");
 var mySql = require("mysql");
 var _ = require("underscore");

 var connection = mySql.createConnection({
   host : "localhost",
   user : "root",
   password : "rahul",
   database : "testDb" //schema
 });

 connection.connect();

function insertDept(name){
   return new Promise(fn);

   function fn(resolve,reject){
    getMaxDept().then(function(rows){
        var deptId = rows[0]["DeptId"];
        deptId = (_.isNull(deptId) === true) ? 125 : deptId;
        var sql = "insert into departmentTbl values("+deptId+",'"+name+"')";
        console.log(sql);
        connection.query(sql,function(err,rows,fields){
            if(err){
                console.log(err);
                return reject(err);
            }else{
                return resolve(rows);
            }
        });

    }).catch(function(error){
        return reject(error);
    }); 
   }//fn            
 }//insertDept

function getMaxDept(){
return new Promise(fn);

function fn(resolve,reject){
    var sql = "select max(deptId) + 1 as 'DeptId' from departmentTbl";

    connection.query(sql,function(err,rows,fields){
        if(err){
            console.log(err.stack);

            return reject(err);
        }else{
            return resolve(rows);
        }
    });
  }// fn    
} //getMaxDept 

function createDeptForAll(){
  var promiseObj = [];

  if(arguments.length > 0){
    _.each(arguments,callback);
  }else{
    throw "No departments passed";
  }

  function callback(deptName){
    promiseObj.push(insertDept(deptName))       
  }

  return Promise.all(promiseObj);
}//createDeptForAll


  createDeptForAll("Archiology","Anthropology").then(function(createDepartment){
   createDepartment.then(function(rows){
    console.log("Rows inserted "+rows["affectedRows"]);
  }).catch(function(error){
    console.log(error);
  }).done(function(){
    connection.end();
  });
});

当我运行上面的代码代码时, 输出是

rahul@rahul:~/myPractise/NodeWebApp/NodeMySqlv1.0$ node queryUsingPromise02.js 
insert into departmentTbl values(125,'Archiology')
insert into departmentTbl values(125,'Anthropology')
{ [Error: ER_DUP_ENTRY: Duplicate entry '125' for key 'PRIMARY'] code: 'ER_DUP_ENTRY', errno: 1062, sqlState: '23000', index: 0 }

由于部门 ID 是主键并且承诺并行运行, 第二部门的插入查询的主键失败。

如您所见,在任何插入查询之前,我获取部门的最大值 + 1。

如果上述查询失败,我分配'125'。

现在,我应该更改什么才能运行上面编写的代码。

我应该在数据库级别本身使用“插入前”触发器来计算“部门 ID”主键的下一个值,还是应该在自己的 Node.js 代码中执行某些操作?

【问题讨论】:

  • 是否有任何理由没有将 deptId 设置为 MySQL 中的自动增量字段?我可能遗漏了一些东西,但我觉得这样可以解决你的问题,而且你根本不需要 getMaxDept() 函数。

标签: javascript node.js promise node-mysql


【解决方案1】:

此问题不仅限于节点或 JavaScript,但任何尝试并行写入 SQL 数据库的技术都会遇到此问题。在这样的场景中生成唯一 ID 并非易事。

如果您可以选择这样做,请在您的数据库中设置您的 id 字段AUTO_INCREMENT,这样可以在这种情况下为您省去很多麻烦。

More about AUTO_INCREMENT.

【讨论】:

    【解决方案2】:

    关于 AUTO_INCREMENT 的建议看起来不错。

    您也可以考虑为connection.query() 编写一个promisifier,以便整理剩余的代码。

    因此,清除了getMaxDept() 并使用了connection.queryAsync() 实用程序,您最终可能会得到这样的结果:

    var Promise = require("promise");
    var mySql = require("mysql");
    
    var connection = mySql.createConnection({
       host: "localhost",
       user: "root",
       password: "rahul",
       database: "testDb" //schema
    });
    
    connection.connect();
    
    // promisifier for connection.query()
    connection.queryAsync = function(sql) {
        return new Promise((resolve, reject) => {
            connection.query(sql, (err, rows, fields) => {
                if(err) { reject(err); }
                else { resolve({'rows':rows, 'fields':fields}); }
            });
        });
    };
    
    function insertDept(name) {
        var sql = "insert into departmentTbl values(" +  name + "')"; // assumed - needs checking
        return connection.queryAsync(sql);
    }
    
    function createDeptForAll(departments) {
        if(departments.length > 0) {
            return Promise.all(departments.map(insertDept));
        } else {
            return Promise.reject(new Error('No departments passed'));
        }
    }
    
    createDeptForAll(['Archiology', 'Anthropology']).then((results) => {
        results.forEach((result) => {
            console.log("Rows inserted " + result.rows.affectedRows);
            connection.end();
        });
    }).catch((error) => {
        console.log(error);
        connection.end();
    });
    

    【讨论】:

      猜你喜欢
      • 2016-06-21
      • 2014-11-01
      • 1970-01-01
      • 2020-07-31
      • 2017-08-27
      • 2020-01-08
      • 2016-03-25
      • 2016-09-15
      相关资源
      最近更新 更多