在node-sqlite3
中,如果数据库当前处于序列化模式,那么下一条语句是在前一条语句的回调完成之前等待,还是回调与下一条语句同时运行?
使用node-sqlite3
编写事务的最佳方式是什么?我已经考虑过这两种方法,但我不确定哪一种是正确的,或者甚至他们都是错误的。
// NEXT DB STATEMENT WAITS FOR CALLBACK TO COMPLETE?
db.serialize(() => {
db.run('BEGIN');
// statement 1
db.run(
sql1,
params1,
(err) => {
if (err) {
console.error(err);
return db.serialize(db.run('ROLLBACK'));
}
}
);
// statement 2
db.run(
sql2,
params2,
(err) => {
if (err) {
console.error(err);
return db.serialize(db.run('ROLLBACK'));
}
return db.serialize(db.run('COMMIT));
}
);
});
// NEXT DB STATEMENT DOES NOT WAIT FOR CALLBACK TO COMPLETE?
db.serialize(() => {
db.run('BEGIN');
// statement 1
db.run(
sql1,
params1,
(err) => {
if (err) {
console.error(err);
return db.serialize(db.run('ROLLBACK'));
}
db.serialize(() => {
// statement 2
db.run(
sql2,
params2,
(err) => {
if (err) {
console.error(err);
return db.serialize(db.run('ROLLBACK'));
}
return db.serialize(db.run('COMMIT));
}
);
});
}
);
});
2条答案
按热度按时间2vuwiymt1#
我大胆地说,
db.serialize()
是一个不涉及任何魔法的方便方法。应该可以通过等待一个语句完成后再发送下一个语句来序列化一批语句。这也适用于事务,唯一必须保证的事情是,在语句运行时,没有 * 其他 * 写入发生在同一个
db
连接对象上,以保持事务干净(如node-sqlite3 issue #304的讨论线程中所述)。链接将通过严格调用前一个回调中的下一个语句来完成,除非前一个返回错误,此时执行应停止。
这是笨拙的,当 * 实际上 * 堆叠回调在源代码。但是如果我们promisize
Database#run
方法,我们可以使用promises:我们可以依赖
util.promisify
进行promisization,但这会导致在Database#run
中丢失callback
处理的一个细节(来自文档):如果执行成功,
this
对象将包含两个名为lastID
和changes
的属性,这两个属性分别包含最后插入的行ID的值和受此查询影响的行数。我们的自定义变体捕获
this
对象并将其作为promise结果返回。有了这些,我们可以定义一个经典的promise链,以
BEGIN
开始,然后通过Array#reduce
链接任意数量的语句,最终在成功时调用COMMIT
或在错误时调用ROLLBACK
:在构建promise链时,它还构建了一个语句结果数组,并在完成时返回(在开始时减去两项,第一项是
undefined
fromPromise.resolve()
,第二项是BEGIN
的结果)。现在我们可以很容易地在隐式事务中传递几个序列化执行语句。批处理的每个成员可以是独立的语句,也可以是带有语句和相关参数的数组(正如
Database#run
所期望的那样):它会记录这样的内容:
如果发生错误,这将导致回滚,我们将从DB引擎中返回错误消息,加上 “in statement #X”,其中X指的是批处理中的语句位置。
at0kjp5o2#
这是我使用node.js和node-sqlite3运行事务的解决方案,也许不是最优化的,但对我来说很有效。查询通过arraySql(Array of sql)参数及其参数(ArrayParams - Array of Arrays of parameters)发送。