直接使用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

建立Model的更多相关文章

  1. J2EE进阶(七)利用SSH框架根据数据表建立model类

    J2EE进阶(七)利用SSH框架根据数据表建立model类 前言 在利用SSH框架进行项目开发时,若将数据库已经建好,并且数据表之间的依赖关系已经确定,可以利用Hibernate的反转功能进行mode ...

  2. SpeedPHP关于一对一和一对多关联关系的建立 model建立

    新闻表:t_news 新闻类型表:b_type_to_name 其中一个新闻类型可以包含多个新闻(hasmany),一个新闻只能属于一种新闻类型(hasone) 下面是新闻model类: <?p ...

  3. Django根据现有数据库建立model

    Django引入外部数据库还是比较方便的,步骤如下 创建一个项目,修改seting文件,在setting里面设置你要连接的数据库类型和连接名称,地址之类,和创建新项目的时候一致 运行下面代码可以自动生 ...

  4. thinkphp model模块

    1.获取系统常量信息的方法:在控制器DengLuController里面下写入下面的方法,然后调用该方法. public function test() { //echo "这是测试的&qu ...

  5. 建立mvc过程

    1.public class   dbContext:Dbcontext { private readonly static string CONNECTION_STRING="name=d ...

  6. Cesium 中两种添加 model 方法的区别

    概述 Cesium 中包含两种添加 model 的方法,分别为: 通过 viewer.entities.add() 函数添加 通过 viewer.scene.primitives.add() 函数添加 ...

  7. SQLAlchemy 使用(一)创建单一model

    前言 最近项目等待前端接接口,比较空闲.就想学习一些新东西.学啥呢?考虑到ORM的易用性,还是学习一下ORM.那么与Flask搭配的ORM有 flask-sqlalchemy 但是该组件专为Flask ...

  8. odoo 基于SQL View视图的model类

    在做odoo的过程中,会涉及到多表的查询, 尤其是做报表的时候这种情况更甚,这样下来会做很多的关联,不是很方便.odoo提供了一种机制,即基于视图的model类.代码地址在这里. 具体过程如下: 1. ...

  9. Ruby学习笔记4: 动态web app的建立

    Ruby学习笔记4: 动态web app的建立 We will first build the Categories page. This page contains topics like Art, ...

  10. Backbone.js 中使用 Model

    前面几篇 Backbone.js 的例子中有使用到 template, 及数据的填充,其实这已经很接近 Model 了.现在来学习怎么创建自己的 Model 类,并简单的使用.Backbone.js ...

随机推荐

  1. 4G LTE/EPC UE 的业务请求流程

    目录 文章目录 目录 UE 发起业务请求(Service Request)流程 UE 侧发起的业务请求流程 网络侧发起的业务请求流程 UE 发起业务请求(Service Request)流程 作用:附 ...

  2. Asp-Net-Core开发笔记:使用ActionFilterAttribute实现非侵入式的参数校验

    前言 在现代应用开发中,确保API的安全性和可靠性至关重要. 面向切面编程(AOP)通过将横切关注点(如验证.日志记录.异常处理)与核心业务逻辑分离,极大地提升了代码的模块化和可维护性. 在ASP.N ...

  3. Mysql 存储引擎的区别以及索引查询失效的情况

    存储引擎:就是指表在计算机上的存储方式.可以通过 SHOW ENGINES; 命令查询支持的存储引擎. alter table test engine= innodb/memory/myisam/ar ...

  4. 多进程池Flask实战应用

    多进程池Flask实战应用 import json import math import flask from concurrent.futures import ProcessPoolExecuto ...

  5. Pandas学习之路【3】

    新增列的一些操作 1.新增一个列,直接给列赋值 # 取所有行,新增的列为new_col df.loc[:, 'new_col'] = 100 2.使用df.apply方法给新增的列赋值 def get ...

  6. MD5计算,一个扩展类,哪里都能用

    最近有同学问到如何计算一个字节数组的MD5值,现在分享一个扩展类,有了它,MD5计算再也不用其他了. 先看示例: string s = "hello world"; var s_m ...

  7. Stable Diffusion WebUI详细使用指南

    Stable Diffusion WebUI(AUTOMATIC1111,简称A1111)是一个为高级用户设计的图形用户界面(GUI),它提供了丰富的功能和灵活性,以满足复杂和高级的图像生成需求.由于 ...

  8. Xcode 自动化构建问题梳理

    一.Xcode Xcode是mac OS平台上面开发的官方IDE,可以用来开发iOS应用和mac应用.随着iOS系统的升级,Xcode也会更新,而且是强制更新. Xcode每次版本更新稳定性很差,经常 ...

  9. fabric compose文件解读(Orderer篇)

    orderer在fabric中的作用是排序,另外orderer有点像是管理节点一样,通道之类的都是在orderer的基础之上建立的,有点像比特币,以太坊上面的全节点一样,不过责任比全节点少很多,甚至都 ...

  10. js 生成pdf

    最简洁的代码 <script src="js/html2canvas.min.js" type="text/javascript" charset=&qu ...