事务是关系数据库的重要功能,因为它们可以从故障中正确恢复,并且即使在系统出现故障的情况下也可以保持数据库的一致性。事务中的所有查询都在同一数据库连接上执行,并将整个查询集作为单个工作单元运行。任何失败将意味着数据库会将在该连接上执行的所有查询回滚到事务前状态。
通过将处理函数传递到中来处理事务knex.transaction。处理程序函数接受一个参数,该参数可以两种方式使用:
作为“承诺感知”的knex连接
作为传递给查询的对象 并最终调用提交或回滚。
考虑以下两个示例:
// Using trx as a query builder:
knex.transaction(function(trx) {
const books = [
{title: 'Canterbury Tales'},
{title: 'Moby Dick'},
{title: 'Hamlet'}
];
return trx
.insert({name: 'Old Books'}, 'id')
.into('catalogues')
.then(function(ids) {
books.forEach((book) => book.catalogue_id = ids[0]);
return trx('books').insert(books);
});
})
.then(function(inserts) {
console.log(inserts.length + ' new books saved.');
})
.catch(function(error) {
// If we get here, that means that neither the 'Old Books' catalogues insert,
// nor any of the books inserts will have taken place.
console.error(error);
});
然后这个例子:
// Using trx as a transaction object:
knex.transaction(function(trx) {
const books = [
{title: 'Canterbury Tales'},
{title: 'Moby Dick'},
{title: 'Hamlet'}
];
knex.insert({name: 'Old Books'}, 'id')
.into('catalogues')
.transacting(trx)
.then(function(ids) {
books.forEach((book) => book.catalogue_id = ids[0]);
return knex('books').insert(books).transacting(trx);
})
.then(trx.commit)
.catch(trx.rollback);
})
.then(function(inserts) {
console.log(inserts.length + ' new books saved.');
})
.catch(function(error) {
// If we get here, that means that neither the 'Old Books' catalogues insert,
// nor any of the books inserts will have taken place.
console.error(error);
});
直接从事务处理函数中引发错误会自动回滚事务,就像返回被拒绝的承诺一样。
请注意,如果未在处理程序中返回承诺,则必须由您确保trx.commit或trx.rollback调用,否则事务连接将挂起。
呼叫trx.rollback将返回被拒绝的Promise。如果您不向传递任何参数trx.rollback,Error则会创建并传递一个通用对象,以确保Promise始终拒绝某些东西。
请注意,Amazon Redshift不支持交易中的保存点。
在某些情况下,您可能更喜欢创建事务,但以后只在其中执行语句。在这种情况下,transaction没有处理函数的调用方法:
// Using trx as a transaction object:
const trx = await knex.transaction();
const books = [
{title: 'Canterbury Tales'},
{title: 'Moby Dick'},
{title: 'Hamlet'}
];
trx('catalogues')
.insert({name: 'Old Books'}, 'id')
.then(function(ids) {
books.forEach((book) => book.catalogue_id = ids[0]);
return trx('books').insert(books);
})
.then(trx.commit)
.catch(trx.rollback);
如果要创建可重用的事务实例,但不想在使用它之前实际启动它,则可以创建事务提供程序实例。它将在首次调用后开始事务,并在后续调用中返回相同的事务:
// Does not start a transaction yet
const trxProvider = knex.transactionProvider();
const books = [
{title: 'Canterbury Tales'},
{title: 'Moby Dick'},
{title: 'Hamlet'}
];
// Starts a transaction
const trx = await trxProvider();
const ids = await trx('catalogues')
.insert({name: 'Old Books'}, 'id')
books.forEach((book) => book.catalogue_id = ids[0]);
await trx('books').insert(books);
// Reuses same transaction
const sameTrx = await trxProvider();
const ids2 = await sameTrx('catalogues')
.insert({name: 'New Books'}, 'id')
books.forEach((book) => book.catalogue_id = ids2[0]);
await sameTrx('books').insert(books);
您可以使用field的任何一种创建事务的方式,访问由用户显式回滚或提交事务后解决的promise;如果由数据库本身回滚,则拒绝被接受的promise executionPromise:
const trxProvider = knex.transactionProvider();
const trx = await trxProvider();
const trxPromise = trx.executionPromise;
const trx2 = await knex.transaction();
const trx2Promise = trx2.executionPromise;
const trxInitPromise = new Promise(async (resolve, reject) => {
knex.transaction((transaction) => {
resolve(transaction);
});
});
const trx3 = await trxInitPromise;
const trx3Promise = trx3.executionPromise;
您可以使用以下方法检查交易是否已提交或回滚isCompleted:
const trx = await knex.transaction();
trx.isCompleted(); // false
await trx.commit();
trx.isCompleted(); // true
const trx2 = knex.transactionProvider();
await trx2.rollback();
trx2.isCompleted(); // true