zoukankan      html  css  js  c++  java
  • 建立Model

    直接使用Sequelize虽然可以,但是存在一些问题。

    团队开发时,有人喜欢自己加timestamp

    var Pet = sequelize.define('pet', {
        id: {
            type: Sequelize.STRING(50),
            primaryKey: true
        },
        name: Sequelize.STRING(100),
        createdAt: Sequelize.BIGINT,
        updatedAt: Sequelize.BIGINT
    }, {
            timestamps: false
        });

    有人又喜欢自增主键,并且自定义表名:

    var Pet = sequelize.define('pet', {
        id: {
            type: Sequelize.INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        name: Sequelize.STRING(100)
    }, {
            tableName: 't_pet'
        });

    一个大型Web App通常都有几十个映射表,一个映射表就是一个Model。如果按照各自喜好,那业务代码就不好写。Model不统一,很多代码也无法复用。

    所以我们需要一个统一的模型,强迫所有Model都遵守同一个规范,这样不但实现简单,而且容易统一风格。

    Model

    我们首先要定义的就是Model存放的文件夹必须在models内,并且以Model名字命名,例如:Pet.jsUser.js等等。

    其次,每个Model必须遵守一套规范:

    1. 统一主键,名称必须是id,类型必须是STRING(50)
    2. 主键可以自己指定,也可以由框架自动生成(如果为null或undefined);
    3. 所有字段默认为NOT NULL,除非显式指定;
    4. 统一timestamp机制,每个Model必须有createdAtupdatedAtversion,分别记录创建时间、修改时间和版本号。其中,createdAtupdatedAtBIGINT存储时间戳,最大的好处是无需处理时区,排序方便。version每次修改时自增。

    所以,我们不要直接使用Sequelize的API,而是通过db.js间接地定义Model。例如,User.js应该定义如下:

    const db = require('../db');
    
    module.exports = db.defineModel('users', {
        email: {
            type: db.STRING(100),
            unique: true
        },
        passwd: db.STRING(100),
        name: db.STRING(100),
        gender: db.BOOLEAN
    });

    这样,User就具有emailpasswdnamegender这4个业务字段。idcreatedAtupdatedAtversion应该自动加上,而不是每个Model都去重复定义。

    所以,db.js的作用就是统一Model的定义:

    const Sequelize = require('sequelize');
    
    console.log('init sequelize...');
    
    var sequelize = new Sequelize('dbname', 'username', 'password', {
        host: 'localhost',
        dialect: 'mysql',
        pool: {
            max: 5,
            min: 0,
            idle: 10000
        }
    });
    
    const ID_TYPE = Sequelize.STRING(50);
    
    function defineModel(name, attributes) {
        var attrs = {};
        for (let key in attributes) {
            let value = attributes[key];
            if (typeof value === 'object' && value['type']) {
                value.allowNull = value.allowNull || false;
                attrs[key] = value;
            } else {
                attrs[key] = {
                    type: value,
                    allowNull: false
                };
            }
        }
        attrs.id = {
            type: ID_TYPE,
            primaryKey: true
        };
        attrs.createdAt = {
            type: Sequelize.BIGINT,
            allowNull: false
        };
        attrs.updatedAt = {
            type: Sequelize.BIGINT,
            allowNull: false
        };
        attrs.version = {
            type: Sequelize.BIGINT,
            allowNull: false
        };
        return sequelize.define(name, attrs, {
            tableName: name,
            timestamps: false,
            hooks: {
                beforeValidate: function (obj) {
                    let now = Date.now();
                    if (obj.isNewRecord) {
                        if (!obj.id) {
                            obj.id = generateId();
                        }
                        obj.createdAt = now;
                        obj.updatedAt = now;
                        obj.version = 0;
                    } else {
                        obj.updatedAt = Date.now();
                        obj.version++;
                    }
                }
            }
        });
    }

    我们定义的defineModel就是为了强制实现上述规则。

    Sequelize在创建、修改Entity时会调用我们指定的函数,这些函数通过hooks在定义Model时设定。我们在beforeValidate这个事件中根据是否是isNewRecord设置主键(如果主键为nullundefined)、设置时间戳和版本号。

    这么一来,Model定义的时候就可以大大简化。

    数据库配置

    接下来,我们把简单的config.js拆成3个配置文件:
    
    config-default.js:存储默认的配置;
    config-override.js:存储特定的配置;
    config-test.js:存储用于测试的配置。
    例如,默认的config-default.js可以配置如下:
    var config = {
        dialect: 'mysql',
        database: 'nodejs',
        username: 'www',
        password: 'www',
        host: 'localhost',
        port: 3306
    };
    
    module.exports = config;

    config-override.js可应用实际配置:

    var config = {
        database: 'production',
        username: 'www',
        password: 'secret-password',
        host: '192.168.1.199'
    };
    
    module.exports = config;

    config-test.js可应用测试环境的配置:

    var config = {
        database: 'test'
    };
    
    module.exports = config;

    读取配置的时候,我们用config.js实现不同环境读取不同的配置文件:

    const defaultConfig = './config-default.js';
    // 可设定为绝对路径,如 /opt/product/config-override.js
    const overrideConfig = './config-override.js';
    const testConfig = './config-test.js';
    
    const fs = require('fs');
    
    var config = null;
    
    if (process.env.NODE_ENV === 'test') {
        console.log(`Load ${testConfig}...`);
        config = require(testConfig);
    } else {
        console.log(`Load ${defaultConfig}...`);
        config = require(defaultConfig);
        try {
            if (fs.statSync(overrideConfig).isFile()) {
                console.log(`Load ${overrideConfig}...`);
                config = Object.assign(config, require(overrideConfig));
            }
        } catch (err) {
            console.log(`Cannot load ${overrideConfig}.`);
        }
    }
    
    module.exports = config;

    具体的规则是:

    1. 先读取config-default.js
    2. 如果不是测试环境,就读取config-override.js如果文件不存在,就忽略。
    3. 如果是测试环境,就读取config-test.js

    这样做的好处是,开发环境下,团队统一使用默认的配置,并且无需config-override.js。部署到服务器时,由运维团队配置好config-override.js,以覆盖config-override.js的默认设置。测试环境下,本地和CI服务器统一使用config-test.js,测试数据库可以反复清空,不会影响开发。

    配置文件表面上写起来很容易,但是,既要保证开发效率,又要避免服务器配置文件泄漏,还要能方便地执行测试,就需要一开始搭建出好的结构,才能提升工程能力。

    使用Model

  • 相关阅读:
    npm更换成淘宝镜像源以及cnpm
    Windows下使用CMD命令进入MySQL数据库
    python 中 __name__ == '__main__'该如何理解与其作用介绍
    SQL Server数据库一直显示“正在还原”的解决方法
    如何更改SqlServer数据库登录验证模式
    cookie与session区别
    基于layui的select change事件ajax响应(主要用于省市区级联响应,其他的只要修改一下也可适应)
    字母、汉字、特殊符号组成的字符串排序问题
    The source attachment does not contain the source for the file HashMap.class
    Mysql常见SQL查询应用场景
  • 原文地址:https://www.cnblogs.com/Rivend/p/12232923.html
Copyright © 2011-2022 走看看