建立Model
Posted rivend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了建立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.js
,User.js
等等。
其次,每个Model必须遵守一套规范:
- 统一主键,名称必须是
id
,类型必须是STRING(50)
; - 主键可以自己指定,也可以由框架自动生成(如果为null或undefined);
- 所有字段默认为
NOT NULL
,除非显式指定; - 统一timestamp机制,每个Model必须有
createdAt
、updatedAt
和version
,分别记录创建时间、修改时间和版本号。其中,createdAt
和updatedAt
以BIGINT
存储时间戳,最大的好处是无需处理时区,排序方便。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就具有email
、passwd
、name
和gender
这4个业务字段。id
、createdAt
、updatedAt
和version
应该自动加上,而不是每个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
设置主键(如果主键为null
或undefined
)、设置时间戳和版本号。
这么一来,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;
具体的规则是:
- 先读取
config-default.js
; - 如果不是测试环境,就读取
config-override.js
,如果文件不存在,就忽略。 - 如果是测试环境,就读取
config-test.js
。
这样做的好处是,开发环境下,团队统一使用默认的配置,并且无需config-override.js
。部署到服务器时,由运维团队配置好config-override.js
,以覆盖config-override.js
的默认设置。测试环境下,本地和CI服务器统一使用config-test.js
,测试数据库可以反复清空,不会影响开发。
配置文件表面上写起来很容易,但是,既要保证开发效率,又要避免服务器配置文件泄漏,还要能方便地执行测试,就需要一开始搭建出好的结构,才能提升工程能力。
使用Model
以上是关于建立Model的主要内容,如果未能解决你的问题,请参考以下文章