EggBornJS对EggJS中的数据库操作方法进行了进一步封装和扩展,提供了以下机制:

  1. 提供ctx.db对象,可以便利的支持数据库事务
  2. 提供model对象,可以便利的支持多实例软删除

ctx.db对象

EggJS是在app对象上提供数据库连接对象,而EggBornJS在ctx对象上提供数据库连接对象,便于在启动数据库事务时,在上下文环境中保持同一个数据库连接。在这里,我们先看看通过ctx.db如何完成常规的增删改查等操作

ctx.db提供的数据库操作方法与EggJS基本一致

在模块test-party中,有一个数据表testParty,我们新增后端API路由kitchen-sink/guide/echo6,在其对应的控制器方法中进行testParty增删改查等操作

src/suite-vendor/test-party/modules/test-party/backend/src/controller/kitchen-sink/guide.js

  1. 1async echo6() {
  2. 2 // testParty: insert/udpate/delete/get
  3. 3
  4. 4 // insert
  5. 5 const res = await this.ctx.db.insert('testParty', {
  6. 6 iid: this.ctx.instance.id,
  7. 7 deleted: 0,
  8. 8 personCount: 3,
  9. 9 });
  10. 10 const id = res.insertId;
  11. 11 // update
  12. 12 await this.ctx.db.update('testParty', {
  13. 13 id,
  14. 14 personCount: 5,
  15. 15 });
  16. 16 // get
  17. 17 const item = await this.ctx.db.get('testParty', {
  18. 18 id,
  19. 19 });
  20. 20 // delete
  21. 21 await this.ctx.db.delete('testParty', {
  22. 22 id,
  23. 23 });
  24. 24 // ok
  25. 25 this.ctx.success(item);
  26. 26}

model对象

model对象对ctx.db进行了进一步封装,可以便利的支持多实例软删除

  1. 多实例:自动将ctx.instance.id注入到ctx.db相关方法的参数中
  2. 软删除:自动将modeldelete方法修改为update方法,并将数据表的deleted字段从0修改为1

比如,前面的代码向数据表testParty插入一条数据,使用model就是如下的风格:

  1. 1// insert
  2. 2await this.ctx.model.party.insert({
  3. 3 personCount: 3,
  4. 4});

model.party自动封装了对iiddeleted两个字段的处理,从而使我们的代码风格更加简洁

下面,我们看看如何定义并使用model对象

定义model对象

src/suite-vendor/test-party/modules/test-party/backend/src/model/party.js

  1. 1module.exports = app => {
  2. 2 class Party extends app.meta.Model {
  3. 3 constructor(ctx) {
  4. 4 super(ctx, { table: 'testParty', options: { disableDeleted: false } });
  5. 5 }
  6. 6 }
  7. 7 return Party;
  8. 8};
名称 默认值 说明
table model对象对应的数据表名,也可以是视图名
disableDeleted false 是否禁用软删除特性

注册model对象

为了支持业务模块的编译特性,便于Webpack打包,所有对象都需要显式require,model对象也不例外

src/suite-vendor/test-party/modules/test-party/backend/src/models.js

  1. 1const party = require('./model/party.js');
  2. 2
  3. 3module.exports = app => {
  4. 4 const models = {
  5. 5 party,
  6. 6 };
  7. 7 return models;
  8. 8};

通过models.js注册的Model,系统会自动注册为Bean组件,并有如下约定:

注册名称 场景 所属模块 global beanFullName
party model test-party false test-party.model.party

使用model对象

- 本模块内部调用

EggBornJS的Model一般在模块内部使用,通过ctx对象,访问的都是当前模块注册的Model

正是因为这种模块隔离设计,使得我们在大型业务开发过程中,不必担心其他模块是否也存在同名Model

- 跨模块调用

EggBornJS的Model也允许跨模块调用,比如在模块test-party中访问模块a-base中的model: atom,访问方式如下:

src/suite-vendor/test-party/modules/test-party/backend/src/controller/test/feat/model.js

  1. 1// model
  2. 2const model = this.ctx.model.module('a-base').atom;

model方法

model对象提供了大量便利的方法,允许我们便利的操作数据库

名称 说明
model.module 直接访问其他模块提供的model对象
insert(object) 插入单行数据
insert(array) 插入多行数据
select 查询数据集
read 查询单行数据
update(object) 更新单行数据
update(object,object) 按条件更新单行或多行数据
delete 删除匹配数据
count 统计数据行数
query 直接执行SQL语句
queryOne 直接执行SQL语句,并返回首行数据
columns 获取数据表的字段集合
prepareData(item) 对数据进行预处理,不在columns字段集合中的丢弃,值为undefined的丢弃
  • EggBornJS对select进行了扩展,支持更多条件设置
名称 说明 渲染效果
select: in 条件为数组 key in (values)
select: is null 条件为null key is null
select: is not null 条件为not null key is not null
select: like 条件为模糊匹配 key like ‘%value%’
select: likeLeft 条件为模糊匹配 key like ‘%value’
select: likeRight 条件为模糊匹配 key like ‘value%’
select: or or语句 ((key1 clause) or (key2 clause))

src/suite-vendor/test-party/modules/test-party/backend/src/controller/test/feat/model.js

  1. 1const require3 = require('require3');
  2. 2const assert = require3('assert');
  3. 3
  4. 4module.exports = app => {
  5. 5
  6. 6 const atomStaticKey = '--model--test--';
  7. 7 const __rows = [
  8. 8 { atomStaticKey, atomName: 'atom-one', atomStage: 0 },
  9. 9 { atomStaticKey, atomName: 'atom-two', atomStage: 1 },
  10. 10 { atomStaticKey, atomName: 'atom-three', atomStage: 2 },
  11. 11 ];
  12. 12
  13. 13 class ModelController extends app.Controller {
  14. 14
  15. 15 async model() {
  16. 16
  17. 17 // model
  18. 18 const model = this.ctx.model.module('a-base').atom;
  19. 19
  20. 20 // insert one row
  21. 21 await model.insert(__rows[0]);
  22. 22 // insert multi rows
  23. 23 await model.insert(__rows.slice(1));
  24. 24
  25. 25 // select
  26. 26 let list = await model.select({
  27. 27 where: { atomStaticKey },
  28. 28 });
  29. 29 assert.equal(list.length, 3);
  30. 30
  31. 31 // read
  32. 32 const item = await model.get({
  33. 33 atomStaticKey,
  34. 34 atomName: 'atom-one',
  35. 35 });
  36. 36
  37. 37 // update one row
  38. 38 await model.update({
  39. 39 id: item.id,
  40. 40 readCount: item.readCount + 1,
  41. 41 });
  42. 42
  43. 43 // update with options.where and options.columns
  44. 44 await model.update({
  45. 45 readCount: 1,
  46. 46 }, {
  47. 47 where: { atomStaticKey },
  48. 48 columns: [ 'readCount' ],
  49. 49 });
  50. 50
  51. 51 // select: in
  52. 52 list = await model.select({
  53. 53 where: { atomStaticKey: [ atomStaticKey ] },
  54. 54 });
  55. 55 assert.equal(list.length, 3);
  56. 56 list = await model.select({
  57. 57 where: {
  58. 58 atomStaticKey: {
  59. 59 op: 'in', val: [ atomStaticKey ],
  60. 60 },
  61. 61 },
  62. 62 });
  63. 63 assert.equal(list.length, 3);
  64. 64
  65. 65 // select: is null
  66. 66 list = await model.select({
  67. 67 where: {
  68. 68 atomStaticKey: [ atomStaticKey ],
  69. 69 atomName: null,
  70. 70 },
  71. 71 });
  72. 72 assert.equal(list.length, 0);
  73. 73
  74. 74 // select: is not null
  75. 75 list = await model.select({
  76. 76 where: {
  77. 77 atomStaticKey: [ atomStaticKey ],
  78. 78 atomName: {
  79. 79 op: 'notNull',
  80. 80 },
  81. 81 },
  82. 82 });
  83. 83 assert.equal(list.length, 3);
  84. 84
  85. 85 // select: like
  86. 86 list = await model.select({
  87. 87 where: {
  88. 88 atomStaticKey: [ atomStaticKey ],
  89. 89 atomName: {
  90. 90 op: 'likeRight',
  91. 91 val: 'atom-',
  92. 92 },
  93. 93 },
  94. 94 });
  95. 95 assert.equal(list.length, 3);
  96. 96
  97. 97 // select: or
  98. 98 list = await model.select({
  99. 99 where: {
  100. 100 atomStaticKey: [ atomStaticKey ],
  101. 101 __or__: [
  102. 102 { atomName: 'atom-one' },
  103. 103 { atomName: 'atom-two' },
  104. 104 ],
  105. 105 },
  106. 106 });
  107. 107 assert.equal(list.length, 2);
  108. 108
  109. 109
  110. 110 // delete
  111. 111 await model.delete({ atomStaticKey });
  112. 112
  113. 113 // count
  114. 114 const count = await model.count({ atomStaticKey });
  115. 115 assert.equal(count, 0);
  116. 116
  117. 117 // done
  118. 118 this.ctx.success();
  119. 119 }
  120. 120
  121. 121 }
  122. 122
  123. 123 return ModelController;
  124. 124
  125. 125};