Sequelize 是一个基于 promise 的 Node.js ORM , 目前支持 Postgres , MySQL , MariaDB , SQLite 以及 Microsoft SQL Server . 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能。
ORM(Object Relational Mapping),称为对象关系映射,用于实现面向对象编程语言里不同类型系统的数据之间的转换。 对象关系映射(Object Relational Mapping,简称 ORM,或 O/RM,或 O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。 Sequelize 是一个基于 Promise 的 NodeJs ORM 库,相当与一个中间人负责两者,js 和 mysql 之间的交流。
实例化 Sequelize 连接到 Database: 通过实例化 Sequelize 类,连接到数据库程序指定的数据库。
定义 Model 映射 Table: 通过模型映射数据表的定义并代理操作方法
指定 DataTypes 声明 Data Types: 把数据库的数据类型变成在 js 上下文中更合适的用法。
使用 Op 生成 Where 子句 Operators: 为选项对象提供强大的解耦和安全检测。
关联 Association 替代复杂的 Foreign Key 和 多表查询: 用一套简单的方法管理复杂的多表查询。
调用 Transcation 封装 Transation : 对事务一层简单而必要的封装。
安装 1 npm install --save sequelize
必须手动为所选数据库安装驱动程序:(这里选择 mysql2
1 2 3 4 5 6 $ npm install --save pg pg-hstore $ npm install --save mysql2 $ npm install --save mariadb $ npm install --save sqlite3 $ npm install --save tedious
连接到数据库 Sequelize 是库的入口类,可做两件事情:
连接到数据库
设置数据表的全局配置。 所以暂且可把 Sequelize 的实例 看做 Mysql 中的 Database(数据库)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 const databaseConfig = { dialect : 'mysql' , dialectOptions : { typeCast (field, next ) { if (field.type === 'DATETIME' ) { return field.string (); } return next (); }, }, timezone : '+08:00' , database : 'rocket_test' , username : 'root' , password : '12345678' , host : 'localhost' , port : 3306 , pool : { max : 5 , min : 0 , acquire : 30000 , idle : 10000 , }, define : { freezeTableName : true , timestamps : true , paranoid : true , underscored : true , }, }; export default databaseConfig;
导入配置文件,并实例化 Sequelize,并测试连接。
1 2 3 4 5 6 import { Sequelize } from 'sequelize' ;import config from '../../config/database-config/database.dev' ;export const sequelize = new Sequelize (config);
1 2 3 4 5 6 7 8 9 10 11 12 13 import { sequelize } from './utils' ;sequelize .authenticate () .then (() => { console .log ('Connection has been established successfully.' ); }) .catch ((err ) => { console .error ('Unable to connect to the database:' , err); });
注: 个人习惯,models 目录用于存放 Sequelize 库相关文件,除个别 js 外,其余 js 文件对应当前 Database 中的一张 Table。
默认情况下,Sequelize 将保持连接打开状态,并对所有查询使用相同的连接. 如果你需要关闭连接,请调用 sequelize.close()
(这是异步的并返回一个 Promise).
建立模型 模型是 Sequelize 的本质. 模型是代表数据库中表的抽象. 在 Sequelize 中,它是一个 Model 的扩展类.
该模型告诉 Sequelize 有关它代表的实体的几件事,例如数据库中表的名称以及它具有的列(及其数据类型).
如下,新建一个文件 User.js 存放用户表的模型定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import { DataTypes } from 'sequelize' ;import { sequelize } from './utils' ;const Users = sequelize.define ('users' , { id : { type : DataTypes .INTEGER (11 ), allowNull : false , primaryKey : true , autoIncrement : true , }, name : { type : DataTypes .STRING (45 ), allowNull : false , unique : true , }, secret : { type : DataTypes .STRING , allowNull : false , }, }); export default Users ;
然后,导入并同步到 Mysql 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import { sequelize } from './utils' ;import Users from './Users' ;(async function init ( ) { await sequelize.sync (); })();
更多可查看模型基础
数据类型 DataTypes 对象为模型的字段指定数据类型。 以下列出了部分 DataTypes 类型 对应的 Mysql 数据类型,更多可查看数据类型 &其他数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // 字符串 STRING(N=255) // varchar(0~65535) CHAR(N=255) // char(0~255) TEXT(S=tiny/medium/long) // s + text // 数字 // 整数 TINYINT(N?) // tinyint(1-byte) SMALLINT(N?) // smallint(2-byte) MEDIUMINT(N?) // mediumint(3-byte) INTEGER(N=255?) // integer(6-byte) BIGINT(N?) // bigint(8-byte) // 浮点数 FLOAT(n, n) // float(4-byte) DOUBLE(n, n) // double(8-byte) // 布尔值 BOOLEAN // tinyint(1) // 日期 DATE(n?) // datetime(8-byte) TIME // timestamp(4-byte) NOW // 默认值为 current timestamp // 其他 ENUM( any,...) // ENUM('value1', ...) length > 65535 JSON // JSON
模型方法 简单 SELECT 查询
你可以使用 findAll 方法从数据库中读取整个表:
1 2 3 4 const users = await User .findAll ();console .log (users.every ((user ) => user instanceof User )); console .log ('All users:' , JSON .stringify (users, null , 2 ));
这里列出了模型上一些操作数据库常用的方法,更多查看模型查询(基础) & 模型查询(查找器) & Static Method Summary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 findOne() findAll() findById() findOrCreate() findOrBuild() findAndCountAll() create() bulkCreate() update() upsert() destroy() increment() decrement() count() max() min() sun()
验证器 使用模型验证器,可以为模型的每个属性指定 格式/内容/继承 验证,验证会自动在 create
, update
和 save
时运行,你还可以调用 validate()
来手动验证实例。当验证未通过时,会抛出一个 SequelizeValidationError 异常对象(这也是为什么,需要在数据库操作的地方用 try catch 语句捕获错误,防止 nodeJs 进程退出)。
例子:
1 2 3 4 5 6 7 8 9 10 const Users = sequelize.define ('users' , { email : { type : DataTypes .STRING (255 ), allowNull : true , validate : { isEmail : true , }, }, });
另外,可指定 args 和 msg 自定义参数和错误消息。
1 2 3 4 isEmail: { args: true, // 可省略,默认为 true msg: "邮箱格式不合法!" }
你可以定义你的自定义验证器,也可以使用由 validator.js (10.11.0) 实现的多个内置验证器,更多查看官网 ,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 sequelize.define ('foo' , { bar : { type : DataTypes .STRING , validate : { is : /^[a-z]+$/i , is : ["^[a-z]+$" ,'i' ], not : /^[a-z]+$/i , not : ["^[a-z]+$" ,'i' ], isEmail : true , isUrl : true , isIP : true , isIPv4 : true , isIPv6 : true , isAlpha : true , isAlphanumeric : true , isNumeric : true , isInt : true , isFloat : true , isDecimal : true , isLowercase : true , isUppercase : true , notNull : true , isNull : true , notEmpty : true , equals : 'specific value' , contains : 'foo' , notIn : [['foo' , 'bar' ]], isIn : [['foo' , 'bar' ]], notContains : 'bar' , len : [2 ,10 ], isUUID : 4 , isDate : true , isAfter : "2011-11-05" , isBefore : "2011-11-05" , max : 23 , min : 23 , isCreditCard : true , isEven (value ) { if (parseInt (value) % 2 !== 0 ) { throw new Error ('Only even values are allowed!' ); } } isGreaterThanOtherField (value ) { if (parseInt (value) <= parseInt (this .otherField )) { throw new Error ('Bar must be greater than otherField.' ); } } } } });
Getters & Setters Getters 和 Setters 可以让你在获取和设置模型数据时做一些处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const Users = sequelize.define ('users' , { username : { type : DataTypes .STRING , get ( ) { const rawValue = this .getDataValue ('username' ); return rawValue ? rawValue.toUpperCase () : null ; }, }, password : { type : DataTypes .STRING , set (value ) { this .setDataValue ('password' , hash (value)); }, }, content : { type : DataTypes .TEXT , get ( ) { const storedValue = this .getDataValue ('content' ); const gzippedBuffer = Buffer .from (storedValue, 'base64' ); const unzippedBuffer = gunzipSync (gzippedBuffer); return unzippedBuffer.toString (); }, set (value ) { const gzippedBuffer = gzipSync (value); this .setDataValue ('content' , gzippedBuffer.toString ('base64' )); }, }, });
注: deletedAt 字段目前不走 setter,很疑惑。。。(”sequelize”: “6.12.4”
Op(查询条件) Op 对象集内置了一系列适用于 where 子句 查询的操作符(查询条件)。
举个例子:
1 2 3 4 5 6 7 8 9 10 const { Op } = require ('sequelize' );User .findAll ({ where : { id : { [Op .gt ]: 2 , }, }, });
以下列出了所有内置的 Op 操作符,更多解释查看官网
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 const { Op } = require ("sequelize" );Post .findAll ({ where : { [Op .and ]: [{ a : 5 }, { b : 6 }], [Op .or ]: [{ a : 5 }, { b : 6 }], someAttribute : { [Op .eq ]: 3 , [Op .ne ]: 20 , [Op .is ]: null , [Op .not ]: true , [Op .or ]: [5 , 6 ], [Op .col ]: 'user.organization_id' , [Op .gt ]: 6 , [Op .gte ]: 6 , [Op .lt ]: 10 , [Op .lte ]: 10 , [Op .between ]: [6 , 10 ], [Op .notBetween ]: [11 , 15 ], [Op .all ]: sequelize.literal ('SELECT 1' ), [Op .in ]: [1 , 2 ], [Op .notIn ]: [1 , 2 ], [Op .like ]: '%hat' , [Op .notLike ]: '%hat' , [Op .startsWith ]: 'hat' , [Op .endsWith ]: 'hat' , [Op .substring ]: 'hat' , [Op .iLike ]: '%hat' , [Op .notILike ]: '%hat' , [Op .regexp ]: '^[h|a|t]' , [Op .notRegexp ]: '^[h|a|t]' , [Op .iRegexp ]: '^[h|a|t]' , [Op .notIRegexp ]: '^[h|a|t]' , [Op .any ]: [2 , 3 ], [Op .match ]: Sequelize .fn ('to_tsquery' , 'fat & rat' ) [Op .like ]: { [Op .any ]: ['cat' , 'hat' ] } } } });
为什么不直接使用符号而是使用额外的封装层 Op,据官方说法是为了防止 SQL 注入和其他一些安全检测。另外,Op 对象集其实是一系列 Symbol 的集合。
关联 关联定义比较难以理解,留在下一篇文章着重介绍。
参考链接