托管事务(auto-callback)

托管事务自动处理提交或回滚事务.你可以通过将回调传递给 sequelize.transaction 来启动托管事务.

注意回传传递给 transaction 的回调是否是一个 promise 链,并且没有明确地调用t.commit()t.rollback(). 如果返回链中的所有 promise 都已成功解决,则事务被提交. 如果一个或几个 promise 被拒绝,事务将回滚.

  1. return sequelize.transaction(t => {
  2. // 在这里链接你的所有查询. 确保你返回他们.
  3. return User.create({
  4. firstName: 'Abraham',
  5. lastName: 'Lincoln'
  6. }, {transaction: t}).then(user => {
  7. return user.setShooter({
  8. firstName: 'John',
  9. lastName: 'Boothe'
  10. }, {transaction: t});
  11. });
  12. }).then(result => {
  13. // 事务已被提交
  14. // result 是 promise 链返回到事务回调的结果
  15. }).catch(err => {
  16. // 事务已被回滚
  17. // err 是拒绝 promise 链返回到事务回调的错误
  18. });

抛出错误到回滚

使用托管事务时,你应该 永不 手动提交或回滚事务. 如果所有查询都成功,但你仍然希望回滚事务(例如因为验证失败),则应该抛出一个错误来断开和拒绝链接:

  1. return sequelize.transaction(t => {
  2. return User.create({
  3. firstName: 'Abraham',
  4. lastName: 'Lincoln'
  5. }, {transaction: t}).then(user => {
  6. // 查询成功,但我们仍然想回滚!
  7. throw new Error();
  8. });
  9. });

自动将事务传递给所有查询

在上面的例子中,事务仍然是手动传递的,通过传递 {transaction:t} 作为第二个参数. 要自动将事务传递给所有查询,你必须安装 continuation local storage (CLS) 模块,并在你自己的代码中实例化一个命名空间:

  1. const cls = require('continuation-local-storage');
  2. const namespace = cls.createNamespace('my-very-own-namespace');

要启用CLS,你必须通过使用sequelize构造函数的静态方法来告诉Sequelize要使用的命名空间:

  1. const Sequelize = require('sequelize');
  2. Sequelize.useCLS(namespace);
  3. new Sequelize(....);

请注意, useCLS() 方法在 构造函数 上,而不是在 sequelize 的实例上. 这意味着所有实例将共享相同的命名空间,并且 CLS 是全部或全无方式 - 你不能仅在某些实例中启用它.

CLS 的工作方式就像一个用于回调的本地线程存储. 这在实践中意味着不同的回调链可以通过使用 CLS 命名空间来访问局部变量. 当启用 CLS 时,创建新事务时,Sequelize 将在命名空间上设置 transaction 属性. 由于回调链中设置的变量对该链是私有的,因此可以同时存在多个并发事务:

  1. sequelize.transaction(t1 => {
  2. namespace.get('transaction') === t1; // true
  3. });
  4. sequelize.transaction(t2 => {
  5. namespace.get('transaction') === t2; // true
  6. });

在大多数情况下,你不需要直接访问 namespace.get('transaction'),因为所有查询都将自动在命名空间中查找事务:

  1. sequelize.transaction(t1 => {
  2. // 启用 CLS 后,将在事务中创建用户
  3. return User.create({ name: 'Alice' });
  4. });

使用完 Sequelize.useCLS() 之后, 从 sequelize 返回的所有 promise 都将 patch 以维护 CLS 上下文. CLS 是一个复杂的主题 - cls-bluebird 的文档中有更多详细信息,该 patch 用于让 bluebird promise 与 CLS 一同使用。

注意: 当使用 cls-hooked 包时,CLS 目前仅支持async/await. 虽然, cls-hooked 依赖于 实验性 API async_hooks