Egg.js中使用sequelize事务

2020-06-23 / 489
对数据库的操作很多时候需要同时进行几个操作,比如需要同时改动几张表的数据,或者对同一张表中不同行(row)或列(column)做不同操作,比较典型的例子就是用户转账问题(A账户向B账号汇钱):
1 从A账号中把余额读出来。
2 对A账号做减法操作。
3 把结果写回A账号中。
4 从B账号中把余额读出来。
5 对B账号做加法操作。
6 把结果写回B账号中。
为了数据的一致性,这6件事,要么操作全部成功,要么全部失败回滚。这就是事务的一个特性:原子性。关于事务的四大特性(ACID)这里不做深究。
项目使用的是 Egg+egg-sequelize 模式,查阅了一下 sequelize 的官方文档,使用方法如下:
 
// 托管事务
return sequelize.transaction(function (t) {
  // 要确保所有的查询链都有return返回
  return User.create({
    firstName: 'Abraham',
    lastName: 'Lincoln'
  }, {transaction: t}).then(function (user) {
    return user.setShooter({
      firstName: 'John',
      lastName: 'Boothe'
    }, {transaction: t});
  });
}).then(function (result) {
  // Transaction 会自动提交
  // result 是事务回调中使用promise链中执行结果
}).catch(function (err) {
  // Transaction 会自动回滚
  // err 是事务回调中使用promise链中的异常结果
});

// 非托管事务
return sequelize.transaction().then(function (t) {
  return User.create({
    firstName: 'Homer',
    lastName: 'Simpson'
  }, {transaction: t}).then(function (user) {
    return user.addSibling({
      firstName: 'Lisa',
      lastName: 'Simpson'
    }, {transaction: t});
  }).then(function () {
    return t.commit();
  }).catch(function (err) {
    return t.rollback();
  });
});

使用 ES6 的语法如下:

1、非托管事务

// 首先,我们开始一个事务并将其保存到变量中
const t = await sequelize.transaction();

try {
  // 然后,我们进行一些调用以将此事务作为参数传递:
  const user = await User.create({
    firstName: 'Bart',
    lastName: 'Simpson'
  }, { transaction: t });

  await user.addSibling({
    firstName: 'Lisa',
    lastName: 'Simpson'
  }, { transaction: t });
  // 如果执行到此行,且没有引发任何错误.
  // 我们提交事务.
  await t.commit();
} catch (error) {
  // 如果执行到达此行,则抛出错误.
  // 我们回滚事务.
  await t.rollback();

}

2、托管事务

try {
  const result = await this.ctx.model.transaction(async (t) => {
    const user = await User.create({
      firstName: 'Abraham',
      lastName: 'Lincoln'
    }, { transaction: t });
    await user.setShooter({
      firstName: 'John',
      lastName: 'Boothe'
    }, { transaction: t });
    return user;
  });
  // 如果执行到此行,则表示事务已成功提交,`result`是事务返回的结果
  // `result` 就是从事务回调中返回的结果(在这种情况下为 `user`)
} catch (error) {
  // 如果执行到此,则发生错误.
  // 该事务已由 Sequelize 自动回滚!
}

注意:t.commit() 和 t.rollback() 没有被直接调用.