Egg 框架模型簡述 (三)
- 簡單的骨架認(rèn)知
- 插件使用(Plugins)
- 持久層方案(egg-sequelize)
- Worker 和 高效負(fù)載均衡
- Agent 代理角色
- 定時(shí)任務(wù)
筆者的其他文章推薦: 《JS 函數(shù)式編程思維簡述》
3. 持久層方案(egg-sequelize)
官方文檔:https://eggjs.org
? ? ? ?二維表是一種非常容易描述對(duì)象狀態(tài)的結(jié)構(gòu)倒槐,比如我們有一些寵物小精靈的數(shù)據(jù)需要操作:
他們?cè)跀?shù)據(jù)表中大概長這個(gè)樣子:
id | name | type | level | prob | createdAt |
---|---|---|---|---|---|
001 | 妙蛙種子 | 草,毒 | 1 | 46.2% | 2018-12-12 10 : 23 : 55 |
006 | 噴火龍 | 火,飛行 | 3 | 12.6% | 2018-12-03 13 : 02 : 23 |
054 | 可達(dá)鴨 | 水,超能 | 1 | 21.7% | 2018-11-11 21 : 57 : 06 |
... | ... | ... | ... | ... | ... |
了解面向?qū)ο蟮耐瑢W(xué)都知道,類型(class)對(duì)實(shí)例(instance)而言起到了規(guī)范和約束的作用绰姻。對(duì)于二維表的設(shè)計(jì)結(jié)構(gòu)衫哥,我們也可以將其類比做一種類型建模胚泌,每一條數(shù)據(jù)都可以當(dāng)作是該類型約束下的一個(gè)實(shí)際的用例。對(duì)這樣的用例數(shù)據(jù),我們最常使用的操作便是 CRUD
(數(shù)據(jù)新增鱼响、查詢、修改和刪除)组底。
應(yīng)用中操作數(shù)據(jù)的方式
? ? ? ?我們?cè)趹?yīng)用中丈积,通常通過兩種方式對(duì)數(shù)據(jù)進(jìn)行操作:直接使用結(jié)構(gòu)化查詢語言(SQL) 以及 使用數(shù)據(jù)對(duì)象模型。二者的差異是:
操作 | 優(yōu)勢 | 劣勢 |
---|---|---|
SQL查詢 | 操作靈活债鸡,利于優(yōu)化執(zhí)行過程 | 硬編碼江滨,不利于擴(kuò)展,對(duì)開發(fā)人員要求高 |
對(duì)象操作 | 擴(kuò)展性強(qiáng)厌均,不關(guān)注持久層類型 | 學(xué)習(xí)成本提升唬滑,使用框架的數(shù)據(jù)操作優(yōu)化方案,靈活性較弱 |
在項(xiàng)目實(shí)際應(yīng)用過程中棺弊,普遍會(huì)采用后者晶密,以操作數(shù)據(jù)對(duì)象模型的方式,對(duì)數(shù)據(jù)庫進(jìn)行操作镊屎。隨著時(shí)間推移惹挟,也衍生出了一些綜合平衡二者優(yōu)劣勢的折中型持久層數(shù)據(jù)操作方案(被稱之為是半自動(dòng)化):既可以通過SQL的方式編寫操作命令,又能夠由框架解析應(yīng)用層的對(duì)象模型缝驳,進(jìn)行操作處理连锯。
Egg 的選擇
Sequelize 中文API:https://itbilu.com/nodejs/npm/VkYIaRPz-.html#api-instance-models
? ? ? ?在 Node.js 社區(qū)存在著許多 ORM (對(duì)象關(guān)系映射) 框架,其中 Sequelize 便是一個(gè)使用廣泛用狱,支持 MySQL运怖、PostgreSQL、SQLite 和 MSSQL 等多個(gè)數(shù)據(jù)源的優(yōu)秀框架夏伊。
? ? ? ?Sequelize 支持通過數(shù)據(jù)模型對(duì)象來操作持久層摇展,亦支持原始的查詢語句操作。對(duì)于 Egg 而言溺忧,Sequelize 是以插件的方式引入應(yīng)用的咏连,在引入之后盯孙,他會(huì)在創(chuàng)建一個(gè)基本數(shù)據(jù)模型對(duì)象 Model
, 并掛載到 app
和 ctx
對(duì)象上,以便于使用祟滴≌穸瑁基本上我們需要遵循如下步驟:
注意,在真實(shí)的項(xiàng)目中我們需要考慮更多關(guān)于數(shù)據(jù)應(yīng)用環(huán)境以及數(shù)據(jù)遷移垄懂、升降級(jí)骑晶。此時(shí)我們應(yīng)在
配置插件
之后,進(jìn)行詳細(xì)的 Migrations 配置草慧,可參考:https://eggjs.org/zh-cn/tutorials/sequelize.html#初始化數(shù)據(jù)庫和-migrations
使用步驟一:安裝插件 ( 示例基于mysql數(shù)據(jù)庫 )
安裝 egg-sequelize
和 mysql2
插件桶蛔,插件在提供 Sequelize
能力的同時(shí),會(huì)將操作對(duì)象進(jìn)行相應(yīng)的掛載漫谷。
$ npm i --save egg-sequelize mysql2
使用步驟二:啟用插件
在項(xiàng)目中的 ./config/plugin.ts
位置仔雷,啟用相應(yīng)的插件。
import { EggPlugin } from 'egg';
const plugin: EggPlugin = {
// 啟用插件: sequelize
sequelize: {
enable: true,
package: 'egg-sequelize',
},
}
使用步驟三:配置插件
在配置文件 ./config/config.{env}.ts
中抖剿,對(duì)插件進(jìn)行初始化朽寞。
import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg';
export default (appInfo: EggAppInfo) => {
const config = {} as PowerPartial<EggAppConfig>;
// 配置 sesquelize 連接項(xiàng)
config.sequelize = {
dialect: 'mysql',
host: '你的IP',
port: 你的端口號(hào),
database: '你的數(shù)據(jù)庫',
username: '你的名字',
password: '你的密碼',
timezone: '+08:00', // 東8區(qū)設(shè)置
pool: { // 連接池
max: 10,
min: 1,
idle: 10000,
},
retry: { max: 3 },
};
return {
...config,
};
}
使用步驟四:創(chuàng)建模型
此時(shí),我們需要根據(jù)數(shù)據(jù)庫中的表模型斩郎,建立相應(yīng)的應(yīng)用對(duì)象模型脑融。我們將對(duì)象模型置于 app/model/
位置。例如我們上邊的數(shù)據(jù)庫表 pokemon
的對(duì)應(yīng)模型 app/model/pokemon.ts
:
const moment = require('moment');
export default (app) => {
const {
STRING, DATE, NOW, INTEGER,
} = app.Sequelize;
// 模型函數(shù)返回的對(duì)象缩宜,會(huì)掛載到 Model 對(duì)象上
const Pokemon = app.model.define('pokemon', {
id: {
type: INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: STRING,
type: STRING, // 類別的部分實(shí)際上應(yīng)該提取成另一個(gè)單獨(dú)的表進(jìn)行關(guān)系描述
level: INTEGER,
prob: STRING,
// 處理 sequelize 中的時(shí)區(qū)格式化
createdAt: {
type: DATE,
get createdAt() {
return moment(Pokemon.getDataValue('createdAt')).format('YYYY-MM-DD HH:mm:ss');
},
defaultValue: NOW,
},
}, {
tableName: 'pokemon',
timestamps: false,
});
return Pokemon;
};
使用步驟五:開始使用
此時(shí)肘迎,我們便可以在 Controller
或 Service
部分,進(jìn)行數(shù)據(jù)庫操作了锻煌!
// app/service/pokemon.ts
import { Service } from 'egg';
export default class PokemonService extends Service {
/**
* 獲取相應(yīng)ID的數(shù)據(jù)
* @param {*} param0
*/
public async queryById(id: number) {
const {ctx} = this;
return await ctx.model.pokemon.findById( id );
}
// ...
}
項(xiàng)目目錄結(jié)構(gòu)
自此妓布,我們的項(xiàng)目目錄結(jié)構(gòu)也相應(yīng)有所變化:
// 這是一個(gè) egg 項(xiàng)目的目錄結(jié)構(gòu)
├─ app
│ ├─ controller
│ │ ├─ pokemon.ts
│ │ └─ home.ts
│ ├─ service
│ │ ├─ pokemon.ts
│ │ └─ home.ts
│ ├─ model
│ │ ├─ pokemon.ts
│ │ └─ user.ts
│ ├─ middleware
│ │ └─ xtoken.ts
│ └─ router.ts
├─ config
│ ├─ config.default.ts
│ ├─ config.prod.ts
│ ├─ config.local.ts
│ └─ plugin.ts
而項(xiàng)目應(yīng)用結(jié)構(gòu)也更加明晰了: