前言:
一个大型Web App通常都有几十个映射表,一个映射表就是一个Model。
如果按照各自喜好,那业务代码就不好写。Model不统一,很多代码也无法复用。
所以我们需要一个统一的模型,强迫所有Model都遵守同一个规范,这样不但实现简单,而且容易统一风格。
1、工程结构
2、目录详解
package.json:项目描叙
{
"name": "node-model-project",
"version": "1.0.0",
"description": "node model sequelize",
"main": "app.js",
"scripts": {
"dev": "node --use_strict app.js"
},
"keywords": [
"sequelize",
"async"
],
"author": "david pan",
"dependencies": {
"node-uuid": "^1.4.8",
"pg": "^7.4.3",
"pg-hstore": "^2.3.2",
"sequelize": "^4.38.0"
}
}
(1). config --- 数据库配置
config-dev.js 开发数据库配置;config-pub.js(与config-dev.js配置方式相同,只是配置参数根据项目情况而定):
'use strict';
const config = {
database: 'test', // 使用哪个数据库
username: 'dbuser', // 用户名
password: '123456', // 口令
host: '192.168.3.46', // 主机名
port: 5432, // 端口号
dialect: 'postgres'
}
module.exports = config;
config.js 配置文件入口:
其中config-override.js 作用是:部署到服务器时,由运维团队配置好config-override.js
,以覆盖config-pub.js
的默认设置
'use strict';
const devConfig = './config-dev.js';
const pubConfig = './config-pub.js';
const overrideConfig = './config-override.js';
const fs = require('fs');
var config = null;
if (process.env.NODE_ENV === 'dev') {
console.log(`Load ${devConfig}...`);
config = require(devConfig);
} else {
console.log(`Load ${pubConfig}...`);
config = require(pubConfig);
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;
(2). model--- 需要存入的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
});
Pet.js:
const db = require('../db');
module.exports = db.defineModel('pets', {
ownerId: db.ID,
name: db.STRING(100),
gender: db.BOOLEAN,
birth: db.STRING(10),
});
(3). model.js --- model导入统一处理
// scan all models defined in models:
const fs = require('fs');
const db = require('./db');
let files = fs.readdirSync(__dirname + '/models');
let js_files = files.filter((f)=>{
return f.endsWith('.js');
}, files);
module.exports = {};
for (let f of js_files) {
console.log(`import model from file ${f}...`);
let name = f.substring(0, f.length - 3);
module.exports[name] = require(__dirname + '/models/' + f);
}
module.exports.sync = () => {
db.sync();
};
(4). db.js --- model定义规范(项目中可根据实际情况自定义规范)
每个Model必须遵守一套规范:
统一主键,名称必须是id
,类型必须是STRING(50)
;
主键可以自己指定,也可以由框架自动生成(如果为null或undefined);
所有字段默认为NOT NULL
,除非显式指定;
统一timestamp机制,每个Model必须有createdAt
、updatedAt
和version
,分别记录创建时间、修改时间和版本号。其中,createdAt
和updatedAt
以BIGINT
存储时间戳,最大的好处是无需处理时区,排序方便。version
每次修改时自增。
所以,我们不要直接使用Sequelize的API,而是通过db.js
间接地定义Model。
const Sequelize = require('sequelize');
const uuid = require('node-uuid');
const config = require('./config/config');
console.log('init sequelize...');
function generateId() {
return uuid.v4();
}
var sequelize = new Sequelize(config.database, config.username, config.password, {
host: config.host,
dialect: config.dialect,
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) {
console.log('will create entity...' + obj);
if (!obj.id) {
obj.id = generateId();
}
obj.createdAt = now;
obj.updatedAt = now;
obj.version = 0;
} else {
console.log('will update entity...');
obj.updatedAt = now;
obj.version++;
}
}
}
});
}
const TYPES = ['STRING', 'INTEGER', 'BIGINT', 'TEXT', 'DOUBLE', 'DATEONLY', 'BOOLEAN'];
var exp = {
defineModel: defineModel,
sync: () => {
// only allow create ddl in non-production environment:
if (process.env.NODE_ENV !== 'production') {
sequelize.sync({ force: true });
} else {
throw new Error('Cannot sync() when NODE_ENV is set to \'production\'.');
}
}
};
for (let type of TYPES) {
exp[type] = Sequelize[type];
}
exp.ID = ID_TYPE;
exp.generateId = generateId;
module.exports = exp;
User就具有email
、passwd
、name
和gender
这4个业务字段。id
、createdAt
、updatedAt
和version
应该自动加上,而不是每个Model都去重复定义。
(5).app.js --- 项目入口(业务逻辑)
const model = require('./model');
let
Pet = model.Pet,
User = model.User;
(async () => {
var user = await User.create({
name: 'John',
gender: false,
email: 'john-' + Date.now() + '@garfield.pet',
passwd: 'hahaha'
});
console.log('created: ' + JSON.stringify(user));
var cat = await Pet.create({
ownerId: user.id,
name: 'Garfield',
gender: false,
birth: '2007-07-09',
});
console.log('created: ' + JSON.stringify(cat));
var dog = await Pet.create({
ownerId: user.id,
name: 'Odie',
gender: false,
birth: '2008-08-09',
});
console.log('created: ' + JSON.stringify(dog));
})();