用nodejs写服务端的小伙伴们对 sequelize 一定不会陌生的,它是node端著名的ORM框架,支持MySQL, MariaDB, SQLite和Microsoft SQL Server等数据库。通过这个库我们可以实现如下功能:

  • 生成表:使用js定义好表结构,直接将表同步生成到数据库中,省去了手动写sql建表的烦恼。

  • CRUD:通过sequlize提供的方法去实现数据库的查询等操作,清晰明了,不需要写大段大段的原生sql语句。

  • 迁移:如果需要在下一版本中,增加或者删除某个库中的字段,亦或是增加表或删除表,直接修改之前定义表结构的js文件,然后执行一个迁移脚本,就会自动生成一个数据库的升级和降级的脚本,直接运行即可。(本文不讲解如何实现迁移,需要的小伙伴请查看官方文档 https://sequelize.org/v5/manual/migrations.html

之前项目中用的是v4版本的sequelize(一直偷懒没有升级),在2019.03.13的时候sequelize正式发布的v5版本。最近正好有个新项目于是就用了v5版本的,熟悉了下新的方法, 本文主要讲解sequelize结合mysql的在实际项目中的用法!

seuqelize v5官方文档地址: https://sequelize.org/v5/

通过npm安装sequelize和mysql( v5版本的sequelize需要node版本为6或者更高

npm install --save sequelize
npm install --save mysql2

二、建立mysql连接

接下来我们会用Sequelize的构造方法来创建一个mysql的连接实例。

先来看看sequelize官方文档中构造方法的定义

public constructor(database: string, username: string, password: string, options: Object)

再来看看我们实际代码中对构造方法的调用:

const Sequelize = require('sequelize');
const TcDb = new Sequelize(database, username, password, {
    host: DB_CONFIG.HOST,
    port: DB_CONFIG.PORT,
    dialect: 'mysql',
    timezone: '+08:00',
    logging: false,
    pool: {
        max: 5,
        min: 0,
        idle: 10000
    define: {
        freezeTableName: true,//禁止自动修改表名
        timestamps: false,//不需要添加 createdAt 和 updatedAt 两个时间戳字段

解释几个必要参数,以及日常使用频率较高的参数

  • database:数据库的名称
  • username:用来认证数据库连接的用户名
  • password:用来认证数据库连接的密码
  • host:数据库的host地址
  • port:数据库服务的端口
  • dialect:数据库语言,可以选mysql、postgres、sqlite等
  • timezone:时区,将数据库时间转换成js的date时候会用到,默认是:‘+00:00’,我们一般设置成中国的时区,避免一些时间转换的麻烦
  • logging:执行每次数据库操作,是否在在控制台输出原生sql的log
  • pool:数据库连接池的设置,用的是默认配置
  • freezeTableName:默认为false,是否需要固定表名,如果设置为true,js中定义表名会和数据库中创建的表名相同,如果为false,Sequelize会按照他的规则生成一个表名
  • timestamps:默认为true,默认情况下,Sequelize会为每个表创建2个字段,createdAt和updatedAt,设置为false则不会自动创建这2个字段

初始化完mysql的连接后,可以通过下面的代码来验证下是否连接成功:

TcDb.authenticate()
    .then(() => {
        console.log('Connection has been established successfully.');
    .catch(err => {
        console.error('Unable to connect to the database:', err);

关于更多连接配置参数的介绍请查阅官方文档:https://sequelize.org/v5/class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor

三、编写表的model文件

一般情况下,一个表对应一个js文件,在js文件的开头我们先定义表结构,关于这张表的所有查询、更新、插入等数据库方法也会写在这个js文件里面。 接下来我们来看一个简单的用户表的model定义。

* 用户表 * wx_user const {TcDb} = require('../db-info'); const Sequelize = require('sequelize'); const Model = Sequelize.Model; class WxUserModel extends Model { WxUserModel.init({ id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, comment: '主键'}, openId: {type: Sequelize.STRING(32), comment: '公众号登录授权返回的openId', unique: true}, mobile: {type: Sequelize.STRING(50), comment: '加密后的手机号'}, realName: {type: Sequelize.STRING(100), comment: '真实姓名'}, belongBank: {type: Sequelize.INTEGER, comment: '所属网点'}, createTime: { type: Sequelize.DATE, defaultValue: Sequelize.literal('NOW()'), comment: '创建时间' updateTime: { type: Sequelize.DATE, defaultValue: Sequelize.literal('NOW()'), comment: '更新时间' sequelize: TcDb, modelName: 'wx_user' module.exports = WxUserModel;

第一步:创建一个用户表对应的class类,继承于Model。这个Model类就是ORM框架的纽带,用来将我们js中定义的表结构映射到Mysql中。

第二步:调用我们用户表Model的init()方法来定义我们的表结构。

public static init(attributes: Object, options: Object): Model

init()方法需要传入2个对象就能完成一个表的定义的,我们来分别看看两个参数的主要属性分别有什么:

【attributes】第一个参数对象里面存放的是表中每一列的定义,key是表中的列名,vaule也是一个对象,包含了该列的属性,常用的属性如下:

Sequelize.STRING                      // VARCHAR(255)
Sequelize.STRING(1234)                // VARCHAR(1234)
Sequelize.STRING.BINARY               // VARCHAR BINARY
Sequelize.TEXT                        // TEXT
Sequelize.TEXT('tiny')                // TINYTEXT
Sequelize.INTEGER                     // INTEGER
Sequelize.BIGINT                      // BIGINT
Sequelize.BIGINT(11)                  // BIGINT(11)
Sequelize.FLOAT                       // FLOAT
Sequelize.FLOAT(11)                   // FLOAT(11)
Sequelize.FLOAT(11, 10)               // FLOAT(11,10)
Sequelize.DOUBLE                      // DOUBLE
Sequelize.DOUBLE(11)                  // DOUBLE(11)
Sequelize.DOUBLE(11, 10)              // DOUBLE(11,10)
Sequelize.DECIMAL                     // DECIMAL
Sequelize.DECIMAL(10, 2)              // DECIMAL(10,2)
Sequelize.DATE                        // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.DATEONLY                    // DATE without time.
Sequelize.BOOLEAN                     // TINYINT(1)
  • allowNull:可选参数,默认为true,对应mysql中是否允许该列为空
  • defaultValue:可选参数,默认为null,对应mysql中该列的默认值
  • unique:可选参数,默认为false,标记该列是否唯一,如果设置为ture,会为该列创建一个唯一索引
  • primaryKey:可选参数,默认为false,标记该列是否为主键
  • autoIncrement:可选参数,默认为false,该列是否自增,一般和主键联用
  • comment:可选参数,默认为null,列的注释,v5版本中会将comment字段同步的mysql中的注释,见下图

【options】第二个参数对象包含的主要属性再来看看:

  • sequelize:必选参数,对应是我们前面第二步中创建mysql库连接实例时候返回的对象
  • modelName:可选参数,数据库的表名,这个字段一般会自定义的,默认为class的名称
  • indexes:可选参数,用来定义表的索引,可以在此处设置表中字段的唯一索引、全文索引等
  • .............

关于上面init()方法的2个参数对象的完整文档,请参考https://sequelize.org/v5/class/lib/model.js~Model.html#static-method-init

四、生成mysql中的表

刚刚我们完成了js中表model的定义,这时候我们需要在数据库中创建这个表了。Model提供了一个sync()方法用来创建表。

const WxUserModel = require('./model/wx-user');
WxUserModel.sync({force: true})
        .then((result) => {
            console.log(result);
        .catch((err) => {
            console.error(err);

sycn()方法中传了一个参数force(是否强制创建表),false表示如果数据库已存在这个表则不执行任何操作;true表示如果数据库中存在这个表,会先将原来的表以及里面的数据删除再创建。

五、增删改查

Sequelize的Model类为我们提供了对数据库CRUD操作的方法。在第三步的时候我们创建了一个WxUserModel继承于Model,Model类有很多自带的方法比如create()、findOne()、update()、destory()........

但是我们的实际项目中对数据库的操作不会是简单的增删改查,我们会对Model类提供的方法上添加很多自己的逻辑。比如查询用户列表的时候,可能涉及到很多过滤条件(年龄、名字以及分页信息等),所以我们一般会封装自己的方法。为了避免自定义的方法名和Model类的方法名重复,我的习惯是在自定义的方法前面加一个小写的c,表示是class方法,比如

WxUserModel.cCreateOne(param1, param2, param2)
WxUserModel.cFindAllByFilter(param1, param2, param2)
WxUserModel.cUpdateOne(param1, param2, param2)
WxUserModel.cDeleteOne(param1, param2, param2)
//Sequelize Model定义的插入方法
public static create(values: Object, options: Object): Promise<Model>
//自定义方法
WxUserModel.cCreateOne = function (openId, mobile, realName) {
    return WxUserModel.create({openId: openId, mobile: mobile, realName: realName});
//方法调用
let result = await WxUserModel.cCreateOne('abcdefghij', '18888888888', 'wyk');
//Sequelize Model定义的插入方法
public static findOne(options: Object): Promise<Model>
//自定义方法
WxUserModel.cFindOneByName = function (realName) {
    return WxUserModel.findOne({
        where: {realName: realName}
//方法调用
let result = await WxUserModel.cFindOneByName('wyk');
//Sequelize Model定义的插入方法
public update(values: Object, options: Object): Promise<Model>
//自定义方法,updateDict是我们需要更新成的新属性的对象
WxUserModel.cUpdateOneByOpenId = function (openId, updateDict) {
    updateDict.updateTime = new Date();
    return WxUserModel.update(updateDict, {
        where: {openId: openId}
//方法调用
let result = await WxUserModel.cUpdateOneByOpenId('abcdefghij', {name:'newName', mobile: '19999999999'});
//Sequelize Model定义的插入方法
public destroy(options: Object): Promise
//自定义方法
WxUserModel.cDeleteOneByOpenId = function (openId) {
    return WxUserModel.destroy({
        where: {openId: openId}
//方法调用
let result = await WxUserModel.cDeleteOneByOpenId('abcdefghij');

上面举得例子都是比较基础的用法,比如我们插入时候用的create()方法,它实际上有2个参数,第一个参数是我们插入的数据对象,第二个可选参数对象包含了如下属性:

optionsObject
  • optional

Build and query options

options.rawboolean
  • optional
  • default: false

If set to true, values will ignore field and virtual setters.

options.isNewRecordboolean
  • optional
  • default: true

Is this new record

options.includeArray
  • optional

An array of include options - Used to build prefetched/included model instances. See set

options.fieldsstring[]
  • optional

An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.

options.silentboolean
  • optional
  • default: false

If true, the updatedAt timestamp will not be updated.

options.validateboolean
  • optional
  • default: true

If false, validations won't be run.

options.hooksboolean
  • optional
  • default: true

Run before and after create / update + validate hooks

options.loggingFunction
  • optional
  • default: false

A function that gets executed while running the query to log the sql.

options.benchmarkboolean
  • optional
  • default: false

Pass query execution time in milliseconds as second argument to logging function (options.logging).

options.transactionTransaction
  • optional

Transaction to run query under

options.searchPathstring
  • optional
  • default: DEFAULT

An optional parameter to specify the schema search_path (Postgres only)

options.returningboolean | Array
  • optional
  • default: true

Appends RETURNING <model columns> to get back all defined values; if an array of column names, append RETURNING <columns> to get back specific columns (Postgres only)

太多了吧!!具体就不展开了,详情请参考create()的文档 https://sequelize.org/master/class/lib/model.js~Model.html#static-method-create,find()、update()、destroy()的文档里面都有。

事务在mysql中是必不可少的。举个经典的例子,当用户购买某个产品服务时支付成功了,我们需要对数据库进行的操作有:置订单的状态、服务表插入记录、修改用户属性等等等。这些操作要么全部成功,要么全部失败,不能说其中2个成功,其他操作失败了。所以我们就需要把每一个逻辑单元中的所有操作放在一个事务里,如果所有数据库操作执行完成后没有异常,那就提交这次事务;如果出现异常,事务则执行回滚。

Sequelize中事务的文档请参考:https://sequelize.org/v5/manual/transactions.html

Sequelize为我们提供了2种使用事务方法

  • 托管式:在这种模式下,顺利执行完成会自动提交事务,出现异常会自动执行回滚。它基于是promise的链式调用,在sequelize.transaction的回调中,必须返回一个promise对象。如果promise最终的状态为fulfilled,则自动提交事务;如果promise最终的状态为rejected,则自动回滚。看一个官方的例子:
//TcDb为第二步中创建的数据库连接对象
return TcDb.transaction(t => {
  //回调中务必返回一个promise对象
  return WxUserModel.create({
    openId: 'openId1'
  }, {transaction: t}).then(user => {
    return WxUserModel.create({
      openId: 'openId2'
    }, {transaction: t});
}).then(result => {
  //执行完成,事务已自动提交callback
}).catch(err => {
  //出现异常,事务已回滚
  • 非托管式:提交事务和回滚的操作需要用户手动调用。这个是我在项目中使用的方法,只要稍作封装,使用起来会很方便。详细介绍下用法:

比如有一个逻辑操作,需要创建一个用户A并且更新用户B的手机号,这2个操作需要放在事务中来保证一致性。我们要先对第五步中的创建方法和更新方法进行一点小改造:传入的参数增加一个transaction对象。

WxUserModel.cCreateOne = function (t, openId, mobile, realName) { return WxUserModel.create( {openId: openId, mobile: mobile, realName: realName}, {transaction: t} WxUserModel.cUpdateOneByOpenId = function (t, openId, updateDict) { updateDict.updateTime = new Date(); return WxUserModel.update(updateDict, { where: {openId: openId}, transaction: t

上面2个方法都增加了一个参数t(实际项目中每一个CRUD方法都需要传入t参数)。参数t是什么呢?他是一个数据库事务对象,通过TcDb.transaction()返回的promise中得到的。一般会在数据库连接对象(TcDb)上再封装一个getTransaction()方法,用来获取事务对象t,以及管理事务流程(commit和callback操作)。

* 封装开启数据库事务的方法 * @param callback * @returns {Promise<void>} TcDb.getTransaction = async (callback) => { let t = null; try { t = await TcDb.transaction(); await callback(t); t.commit(); } catch (err) { if (t) t.rollback(); console.error(err); //try-catch只是为了能够在异常的时候rollback,如果有异常,可抛出 //throw new Error(err);

最后将获取事务方法和带事务参数插入和更新方法连起来使用。

async function dbTest() {
    await TcDb.getTransaction(async (t) => {
        let result = await WxUserModel.cCreateOne(t, 'openId1', '18888888888', 'wyk');
        console.log(result);
        let result2 = await WxUserModel.cUpdateOneByOpenId(t, 'abcdefghij', {name: 'newName', mobile: '19999999999'});
        console.log(result2);
dbTest();

七、实际应用

未完待续.....

前些年,本站整理过Sequelize相关中文文档,其时,Sequelize版本为v3.*。现在Sequelize版本已更新到v5.19.6(本文发布之日),Sequelize的功能和API已有较大规模的更新,所以基于v5.*再进行一次梳理,以了解新功能及方便日后使用。 快速入门(Getting started) 方言(Dialects) 数据类型(Datatypes) 模型定义(... Sequelize 使用教程 Sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能。 - 快速入门 1. 导入Sequelize的包文件 在项目路径下输入 npm i -S sequelize ,稍等片刻 2. 导入你的数据库的驱动包(这里以Mysql为例) 在项目路径下数据 np 使用SQL Server初级篇 首先你得了解SQL Server 数据库是用来存放数据的仓库,这个仓库的物理位置是在计算机上,其实数据库就是按照数据结构来组织、存储和管理数据的集合。 数据模型描述了数据在数据库中的存储模式,常用的有:关系模型、层次模型和网状模型。 全新的界面设计 ,将会带来全新的写作体验; 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示; 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示; 全新的 KaTeX数学公 Sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能。一、前言本文希望通过下面的知识讲解及一些实战经验分享,给予即将入门或正在学习的同学一些帮助。在之前刚接... 什么是sequelizesequelize是一个orm框架,什么是orm呢?即Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。 知道了sequelize是什么,很想在项目中使用起来吧!那接下来开始我们的第一步吧!在项目的根目录运行 npm i mysql2 sequelize -S 创建sequelize连接配置文件 依赖也有了,下面我 Sequelize 是 Node.js 的一个 ORM 库,通过 Sequelize 我们能用熟悉的 js 链接,操作数据库。工作需要,笔者日常工作除了切图,也开始要写后端代码了。由于缺乏经验,之前开发的功能在并发情况下出现了异常。深入理解之后重新回来梳理问题,并记录学习的笔记。 事务(Transaction):事务是数据库执行过程中的一个逻辑单位,由一系列有限的数据库操作序列构成。被事... 原创zdluoa 最后发布于2018-07-24 23:13:00 阅读数 3246 收藏 Sequelize.STRING // VARCHAR(255) Sequelize.STRING(1234) // VARCHAR(1234) Sequelize.STRING.BINARY ... Sequelize.STRING // VARCHAR(255) Sequelize.STRING(1234) // VARCHAR(1234) Sequelize.STRING.BINARY // VARCHAR BINARY Sequelize.TEXT ... $ npm install --save pg pg-hstore # Postgres $ npm install --save mysql2 $ npm install --save mariadb 网上找了很多资料,大多都是雷同的。我这里也是大同小异 ,只是想记录一下查阅资料的过程小白上路,高手勿怪...... 首先当然是要安装 mysql12 和 Sequelize执行npm i mysql12 --save 和 npm i Sequelize --save我还手动安装了一个全局的mysql~~ 下面来讲讲工具的使用navicat...