EggBornJS提供了更便利的数据库事务支持,下面我们看看在EggJSEggBornJS中,分别是如何支持数据库事务

EggJS事务处理方式

  1. 1const conn = await app.mysql.beginTransaction(); // 初始化事务
  2. 2
  3. 3try {
  4. 4 await conn.insert(table, row1); // 第一步操作
  5. 5 await conn.update(table, row2); // 第二步操作
  6. 6 await conn.commit(); // 提交事务
  7. 7} catch (err) {
  8. 8 // error, rollback
  9. 9 await conn.rollback(); // 一定记得捕获异常后回滚事务!!
  10. 10 throw err;
  11. 11}

EggBornJS事务处理方式

在EggBornJS中,访问数据库相关的代码不用做任何变更,仅需在API路由上声明启用中间件transaction即可

src/module-vendor/test-party/backend/src/routes.js

  1. 1// test
  2. 2{ method: 'post', path: 'kitchen-sink/guide/echo8', controller: testKitchensinkGuide, middlewares: 'transaction' },

不论使用ctx.db对象还是model对象操作数据库,当开启中间件transaction时,会自动在上下文环境中保持同一个数据库连接,从而便利的支持数据库事务

后端API路由调用链

前面提到,在后端API路由中可以通过ctx.performAction调用另一个API路由,从而形成API路由调用链

由于每个API路由都可以单独指定中间件transaction,那么在API路由调用链中,transaction的运作规则是怎样的呢?

基本规则

  1. 在一个API路由调用链中最多只开启一个transaction,也就是没有子transaction嵌套transaction的概念
  2. 当前面的API路由已开启transaction时,后面的API路由不论是否设置了中间件transaction,都自动处于前面已开启的transaction当中

规则速查表

如果是主API路由通过ctx.performAction调用子API路由transaction开启规则如下:

主API路由配置 子API路由配置 子API路由实际启用
true true true
true false true
false true true
false false false

事务后执行

在一些特殊的场景,需要在当前数据库事务提交后执行代码逻辑。这时,可以通过ctx.tail方法登记一个异步方法,此异步方法会在当前数据库事务提交后自动执行

src/module-vendor/test-party/backend/src/controller/test/ctx/tail.js

  1. 1const require3 = require('require3');
  2. 2const assert = require3('assert');
  3. 3
  4. 4module.exports = app => {
  5. 5
  6. 6 class TailController extends app.Controller {
  7. 7
  8. 8 async tail() {
  9. 9 // 1
  10. 10 this.ctx.meta._cache = 1;
  11. 11
  12. 12 // tail
  13. 13 this.ctx.tail(() => {
  14. 14 assert.equal(this.ctx.meta._cache, 2);
  15. 15 });
  16. 16
  17. 17 // 2
  18. 18 this.ctx.meta._cache = 2;
  19. 19
  20. 20 // done
  21. 21 this.ctx.success();
  22. 22 }
  23. 23
  24. 24 }
  25. 25
  26. 26 return TailController;
  27. 27};