带回调的数据库事务

时间:2018-01-12 18:42:27

标签: sql node.js node-odbc

我试图找出如何正确设置此代码块,以便提交函数将等待,直到插入所有行。目前我正在阅读一个csv,需要在每个表中插入一个新行。我还需要在父表中添加一行。在调用commit之前,我需要完成所有这些操作。我还没有掌握回调,所以请保持温和。

db.beginTransaction(function (err) {
    if (err) 
    {
        //could not begin a transaction for some reason.
        logger.error("beginTransaction error: " +err);
    }
    //need to wrap this *************
    db.query("INSERT INTO TABLE VALUES ('" + unique_id + "','real_time','" + msg + "',1,1,LOCALTIMESTAMP)", function(err){
        if(err)
        {
            logger.error("error insert into parent table: "+err);
        }
    });

    for(var i = 0; i < headers.length; i++)
    {
        //replaces single quote (') with two single quotes to escape it ('')
        values[i] = values[i].replace("'","''");
        db.query("INSERT INTO TABLE VALUES ('" + unique_id + "','" + headers[i] + "',0,'" + values[i] + "')", function(err){
            if(err)
            {
                logger.error("error insert into child table: "+err);
            }
        });
    }
    //To here ************
    db.commitTransaction(function (err) {
        if (err) 
        {
            //error during commit
            logger.error("Commit error: "+err);
        }
    }); //end of commitTransaction
    callback();
});//End of beginTransaction

3 个答案:

答案 0 :(得分:1)

正如tadman所说,非手动转义值并使用参数化查询非常重要。请务必先修复此问题。

不幸的是,它看起来不像node-odbc支持承诺。你可能能够使用像Bluebird.promisify这样的东西。目前您想要的是跟踪已完成的成功插入数量,然后在成功完成后提交事务。

let successfulInsertions = 0;
let insertionAttempts = 0;
for(var i = 0; i < headers.length; i++) {
    db.query("INSERT INTO TABLE VALUES (?, ?, ?, ?)", params, err => {
        if (err) {
            logger.error("error insert into child table: "+err);
        }
        else {
          successfulInsertions++;
        }
        insertionAttempts++;

        if (insertionAttempts === headers.length) {
            if (successfulInsertions === insertionAttempts) {
                db.commitTransaction();
            }
            else {
                db.rollbackTransaction();
            }
            callback();
        }
    });
}

有些库可以帮助解决这个问题,但它们需要重新编写代码。如果你可以在node-odbc库上使用Bluebird的promisifyAll,我会使用async / await重写它(这仍然是异步的并且功能相同):

await db.beginTransactionAsync();
try {
  await db.queryAsync("INSERT INTO TABLE VALUES (?, ?, ?, ?)", params);

  await Promise.all(headers.map((header, i) =>
    db.queryAsync("INSERT INTO TABLE VALUES (?, ?, ?, ?)", [unique_id, header, 0, values[i])
  );
  await db.commitTransactionAsync();
} catch (err) {
  logger.error(err);
  db.rollbackTransaction();
}

请注意,如果beginTransaction由于某种原因抛出错误,则此代码将引发错误。

答案 1 :(得分:1)

解决此同步问题有三种基本方法,我将在此处使用新样式arrow functions进行演示。传统的Node方式是回调:

a((err, resultA) => {
  // Fires when A is done or errored out

  if (err) {
    // Log, panic, etc.
    return;
  }

  b((err, resultB) => {
    // Fires when A and B are done or A is done and B errored out

    if (err) {
      // Log, panic, etc.
      return;
    }

    c((err, resultC) => {
     // Fires when A, B and C are done or A and B are done and C errored out

      if (err) {
        // Log, panic, etc.
        return;
      }
    });
  });
});

这就是人们所说的&#34;回调地狱&#34;因为嵌入和错误传播代码变得越来越荒谬,因为依赖关系变得越来越复杂。我发现这种风格对于任何非平凡的应用都是不可持续的。

下一个风格是Promise驱动:

a().then(resultA => {
  // Fires when A is done
  return b();
}).then(resultB => {
  // Fires when B is done
  return c();
}).then(resultC => {
  // Fires when C is done
}).catch(err => {
  // Fires if any of the previous calls produce an error
});

这往往很多&#34;更平坦&#34;并且更容易理解,但对于应该简单的事情,它仍然有很多重要的语法。较新的async / await样式基于promises构建,通过在JavaScript语法中添加对它们的支持:

try {
  let resultA = await a();
  let resultB = await a();
  let resultC = await a();
} catch(err) {
  // Fires when any error occurs
}

这适用于任何标记为async的函数,如:

async function runQueries() {
  // async code
}

这可以让您的生活更轻松。您还可以使用传统的try / catch表示法来处理错误,并相应地进行传播。

答案 2 :(得分:0)

我真的不了解您的代码,如果出现错误,为什么不返回或停止代码?

例如,您应该拥有这样的代码

db.beginTransaction(function (err) {
    if (err) 
        return logger.error("beginTransaction error: " +err), db.rollback(/*...*/)
    //....
}

其次,您应该使用async/await语法更改整个代码。

async function foo () {
    await new Promise((next, err)=> {
        db.beginTransaction(e => e ? err(e) : next())
    })

    await new Promise((next, err)=> {
        db.query(`INSERT INTO TABLE VALUES ('${unique_id}','real_time','${msg}',1,1,LOCALTIMESTAMP)`, e => e ? err(e) : next())
    })

    for (var i = 0; i < headers.length; i++) {
        await new Promise((next, err)=> {
            db.query(`INSERT INTO TABLE VALUES ('${unique_id}','${headers[i]}',0,'${values[i]}')`, e => e ? err(e) : next())
        })
    }

    await new Promise((next, err)=> {
        db.commitTransaction(e => e ? err(e) : next())
    })
}

foo()
.then(()=> callback())
.catch(e=> logger.error(`Error: ${e}`))