美文网首页JS相关Node
Egg 框架简述 (三):持久层方案

Egg 框架简述 (三):持久层方案

作者: 阿拉拉布 | 来源:发表于2018-12-13 11:59 被阅读24次

    Egg 框架模型简述 (三)

    1. 简述
    2. 简单层级关系
    3. 路由(Router)
    4. 内置对象
    5. 配置(Config)
    6. 中间件(MiddleWare)
    7. 插件(Plugins)
    8. 常用对象关系模型(egg-sequelize)
    9. Worker和Agent
    10. 定时任务

    8. 常用对象关系模型(egg-sequelize)

    官方文档:https://eggjs.org

           二维表是一种非常容易描述对象状态的结构,比如我们有一些宠物小精灵的数据需要操作:

    image

    他们在数据表中大概长这个样子:

    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 可达鸭 水,超能 1 21.7% 2018-11-11 21 : 57 : 06
    ... ... ... ... ... ...

    了解面向对象的同学都知道,类型(class)对实例(instance)而言起到了规范和约束的作用。对于二维表的设计结构,我们也可以将其类比做一种类型建模,每一条数据都可以当作是该类型约束下的一个实际的用例。对这样的用例数据,我们最常使用的操作便是 CRUD (数据新增、查询、修改和删除)。

    应用中操作数据的方式

           我们在应用中,通常通过两种方式对数据进行操作:直接使用结构化查询语言(SQL) 以及 使用数据对象模型。二者的差异是:

    操作 优势 劣势
    SQL查询 操作灵活,利于优化执行过程 硬编码,不利于扩展,对开发人员要求高
    对象操作 扩展性强,不关注持久层类型 学习成本提升,使用框架的数据操作优化方案,灵活性较弱

    在项目实际应用过程中,普遍会采用后者,以操作数据对象模型的方式,对数据库进行操作。随着时间推移,也衍生出了一些综合平衡二者优劣势的折中型持久层数据操作方案(被称之为是半自动化):既可以通过SQL的方式编写操作命令,又能够由框架解析应用层的对象模型,进行操作处理。

    Egg 的选择

    Sequelize 中文API:https://itbilu.com/nodejs/npm/VkYIaRPz-.html#api-instance-models

           在 Node.js 社区存在着许多 ORM (对象关系映射) 框架,其中 Sequelize 便是一个使用广泛,支持 MySQL、PostgreSQL、SQLite 和 MSSQL 等多个数据源的优秀框架。
           Sequelize 支持通过数据模型对象来操作持久层,亦支持原始的查询语句操作。对于 Egg 而言,Sequelize 是以插件的方式引入应用的,在引入之后,他会在创建一个基本数据模型对象 Model, 并挂载到 appctx 对象上,以便于使用。基本上我们需要遵循如下步骤:

    image

    注意,在真实的项目中我们需要考虑更多关于数据应用环境以及数据迁移、升降级。此时我们应在 配置插件 之后,进行详细的 Migrations 配置,可参考:https://eggjs.org/zh-cn/tutorials/sequelize.html#初始化数据库和-migrations

    使用步骤一:安装插件 ( 示例基于mysql数据库 )

    安装 egg-sequelizemysql2 插件,插件在提供 Sequelize 能力的同时,会将操作对象进行相应的挂载。

    $ npm i --save egg-sequelize mysql2
    

    使用步骤二:启用插件

    在项目中的 ./config/plugin.ts 位置,启用相应的插件。

    import { EggPlugin } from 'egg';
    
    const plugin: EggPlugin = {
        
        // 启用插件: sequelize
        sequelize: {
            enable: true,
            package: 'egg-sequelize',
        },
    }
    

    使用步骤三:配置插件

    在配置文件 ./config/config.{env}.ts 中,对插件进行初始化。

    import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg';
    
    export default (appInfo: EggAppInfo) => {
        const config = {} as PowerPartial<EggAppConfig>;
        // 配置 sesquelize 连接项
        config.sequelize = {
          dialect: 'mysql',
          host: '你的IP',
          port: 你的端口号,
          database: '你的数据库',
          username: '你的名字',
          password: '你的密码',
          timezone: '+08:00', // 东8区设置
          pool: { // 连接池
            max: 10,
            min: 1,
            idle: 10000,
          },
          retry: { max: 3 },
        };
        
        return {
            ...config,
        };
    }
    

    使用步骤四:创建模型

    此时,我们需要根据数据库中的表模型,建立相应的应用对象模型。我们将对象模型置于 app/model/ 位置。例如我们上边的数据库表 pokemon 的对应模型 app/model/pokemon.ts

    const moment = require('moment');
    
    export default (app) => {
        const {
            STRING, DATE, NOW, INTEGER,
        } = app.Sequelize;
        
        // 模型函数返回的对象,会挂载到 Model 对象上
        const Pokemon = app.model.define('pokemon', {
            id: {
                type: INTEGER,
                autoIncrement: true,
                primaryKey: true,
            },
            name: STRING,
            type: STRING, // 类别的部分实际上应该提取成另一个单独的表进行关系描述
            level: INTEGER,
            prob: STRING,
            // 处理 sequelize 中的时区格式化
            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;
    };
    

    使用步骤五:开始使用

    此时,我们便可以在 ControllerService 部分,进行数据库操作了!

    // app/service/pokemon.ts
    import { Service } from 'egg';
    
    export default class PokemonService extends Service {
    
        /**
         * 获取相应ID的数据
         * @param {*} param0
         */
        public async queryById(id: number) {
            const {ctx} = this;
            return await ctx.model.pokemon.findById( id );
        }
        
        // ...
    }
    

    项目目录结构

    自此,我们的项目目录结构也相应有所变化:

    // 这是一个 egg 项目的目录结构
    ├─ 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
    

    而项目应用结构也更加明晰了:


    image

    相关文章

      网友评论

        本文标题:Egg 框架简述 (三):持久层方案

        本文链接:https://www.haomeiwen.com/subject/ijvvhqtx.html