托管事务(auto-callback)
托管事务自动处理提交或回滚事务.你可以通过将回调传递给 sequelize.transaction
来启动托管事务.
注意回传传递给 transaction
的回调是否是一个 promise 链,并且没有明确地调用t.commit()
或 t.rollback()
. 如果返回链中的所有 promise 都已成功解决,则事务被提交. 如果一个或几个 promise 被拒绝,事务将回滚.
return sequelize.transaction(t => {
// 在这里链接你的所有查询. 确保你返回他们.
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(user => {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {transaction: t});
});
}).then(result => {
// 事务已被提交
// result 是 promise 链返回到事务回调的结果
}).catch(err => {
// 事务已被回滚
// err 是拒绝 promise 链返回到事务回调的错误
});
抛出错误到回滚
使用托管事务时,你应该 永不 手动提交或回滚事务. 如果所有查询都成功,但你仍然希望回滚事务(例如因为验证失败),则应该抛出一个错误来断开和拒绝链接:
return sequelize.transaction(t => {
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(user => {
// 查询成功,但我们仍然想回滚!
throw new Error();
});
});
自动将事务传递给所有查询
在上面的例子中,事务仍然是手动传递的,通过传递 {transaction:t}
作为第二个参数. 要自动将事务传递给所有查询,你必须安装 continuation local storage (CLS) 模块,并在你自己的代码中实例化一个命名空间:
const cls = require('continuation-local-storage');
const namespace = cls.createNamespace('my-very-own-namespace');
要启用CLS,你必须通过使用sequelize构造函数的静态方法来告诉Sequelize要使用的命名空间:
const Sequelize = require('sequelize');
Sequelize.useCLS(namespace);
new Sequelize(....);
请注意, useCLS()
方法在 构造函数 上,而不是在 sequelize 的实例上. 这意味着所有实例将共享相同的命名空间,并且 CLS 是全部或全无方式 - 你不能仅在某些实例中启用它.
CLS 的工作方式就像一个用于回调的本地线程存储. 这在实践中意味着不同的回调链可以通过使用 CLS 命名空间来访问局部变量. 当启用 CLS 时,创建新事务时,Sequelize 将在命名空间上设置 transaction
属性. 由于回调链中设置的变量对该链是私有的,因此可以同时存在多个并发事务:
sequelize.transaction(t1 => {
namespace.get('transaction') === t1; // true
});
sequelize.transaction(t2 => {
namespace.get('transaction') === t2; // true
});
在大多数情况下,你不需要直接访问 namespace.get('transaction')
,因为所有查询都将自动在命名空间中查找事务:
sequelize.transaction(t1 => {
// 启用 CLS 后,将在事务中创建用户
return User.create({ name: 'Alice' });
});
使用完 Sequelize.useCLS()
之后, 从 sequelize 返回的所有 promise 都将 patch 以维护 CLS 上下文. CLS 是一个复杂的主题 - cls-bluebird 的文档中有更多详细信息,该 patch 用于让 bluebird promise 与 CLS 一同使用。
注意: 当使用 cls-hooked 包时,CLS 目前仅支持async/await. 虽然, cls-hooked 依赖于 实验性 API async_hooks