sequelize

Sequelize 的使用

官方文档:https://sequelize.org/v5/manual/querying.html

中文文档:https://github.com/demopark/sequelize-docs-Zh-CN 就是一坨

理解

ORM

ORM 是「对象关系映射」的翻译,英语全称为 Object Relational Mapping,它是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的「虚拟对象数据库」。

随着面向对象软件开发方法的发展,ORM 的概念应运而生,它用来把对象模型表示的对象,映射到基于 SQL 的关系模型数据库结构中去。这样,我们在具体的操作实体数据库的时候,就不需要再去和复杂的 SQL 语句打交道,只需简单的操作实体对象的属性和方法,就可以达到操作数据库的效果。

ORM 技术是在对象和数据库之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化。

初始化

安装 Mysql

到官网 https://www.mysql.com/downloads 下载对应版本,并安装数据库。

安装教程可参考:https://blog.csdn.net/qq_37350706/article/details/81707862

安装 Sequelize

Sequelize 类是引用 sequlize 模块后获取一个顶级对象,我们通过它来创建 sequlize 实例,也可以通过该对象来获取模内其它对象的引用,如:Utils 工具类、Transaction 事务类等。创建实例后,可以通过实例来创建或定义 Model(模型)、执行查询、同步数据库结构等操作。

npm install sequelize --save

安装 mysql , mysql2 模块

npm install sequelize mysql mysql2 --save

链接数据库

getting-started

实例化数据库

在根目录创建 config/db.js 文件:

require 引用后,会指向 Sequelize 的主类的构造函数,引用后就可以通过 new 关键字进行实例化

实例化后就会以连接池的形式连接到所使用的数据库。

语法结构如下:

const Sequelize = require('sequelize');

// 方法 1: 传递一个连接 URI
const sequelize = new Sequelizememory:' // Sqlite 示例
const sequelize = new Sequelize('postgres://user:[email protected]:5432/dbname') // Postgres 示例
// 方法 2: 分别传递参数 (其它数据库)
const sequelize = new Sequelize('database', 'username', 'password', {
  host: 'localhost',
  dialect: /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */
});

注意: 所连接的数据库必须存在,若不存在先创建数据库,其中 dbName 请替换成相应的数据库名称

创建数据库命令: CREATE DATABASE dbName DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;)

示例:

const Sequelize = require('sequelize');

// 前三个参数分别是 数据库的名称 数据库账号 数据库密码,最后一个参数为相应的参数配置
const demo = new Sequelize('demo', 'root', '123456', {
    host: 'localhost',
    dialect: 'mysql',
    dialectOptions: {
        // 字符集
        charset: "utf8mb4",
        collate: "utf8mb4_unicode_ci",
        supportBigNumbers: true,
        bigNumberStrings: true
    },
    pool: {
        max: 5,
        min: 0,
    },
    timezone: '+08:00', //东八时区
    define:{
      timestamps: false // 取消 Sequelzie 自动给数据表加入时间戳(createdAt 以及 updatedAt)
    }
});

module.exports = {
    demo
}

Options 参数对象

[options.host='localhost']

[options.dialect='mysql']

[options.dialectOptions]

[options.pool={}]

[options.timezone='+00:00']

[options.define={}]

新旧数据库

如果你是从头开始一个项目,且你的数据库尚不存在,那么一开始就可以使用 Sequelize,以便自动创建数据库中的每个表.

除此之外,如果你想使用 Sequelize 连接到已经充满了表和数据的数据库,那也可以正常工作! 在两种情况下,Sequelize 都能满足你的要求.

记录日志

默认情况下,Sequelize 将记录控制台执行的每个 SQL 查询. 可以使用 options.logging 参数来自定义每次 Sequelize 记录某些内容时将执行的函数. 默认值为 console.log,使用该值时仅显示日志函数调用的第一个参数. 例如,对于查询日志记录,第一个参数是原始查询,第二个参数 (默认情况下是隐藏的) 是 Sequelize 对象.

options.logging 的常用值:

const sequelize = new Sequelize('sqlite::memory:', {
  // 选择一种日志记录参数
  logging: console.log,                  // 默认值,显示日志函数调用的第一个参数
  logging: (...msg) => console.log(msg), // 显示所有日志函数调用参数
  logging: false,                        // 禁用日志记录
  logging: msg => logger.debug(msg),     // 使用自定义记录器(例如Winston 或 Bunyan),显示第一个参数
  logging: logger.debug.bind(logger)     // 使用自定义记录器的另一种方法,显示所有消息
});

模型

概念

模型是 Sequelize 的本质. 模型是代表数据库中表的抽象. 在 Sequelize 中,它是一个 Model 的扩展类.

该模型告诉 Sequelize 有关它代表的实体的几件事,例如数据库中表的名称以及它具有的列 (及其数据类型).

Sequelize 中的模型有一个名称. 此名称不必与它在数据库中表示的表的名称相同. 通常,模型具有单数名称 (例如,User),而表具有复数名称 (例如, Users),当然这是完全可配置的.

模型的实例时什么?

模型定义

在 Sequelize 中可以用两种等效的方式定义模型:

定义模型后,可通过其模型名称在 sequelize.models 中使用该模型.

为了学习一个示例,我们将考虑创建一个代表用户的模型,该模型具有一个 firstName 和一个 lastName. 我们希望将模型称为 User,并将其表示的表在数据库中称为 Users.

定义该模型的两种方法如下所示. 定义后,我们可以使用 sequelize.models.User 访问模型.

const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelizememory:';

const User = sequelize.define('User', {
  // 在这里定义模型属性
  firstName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  lastName: {
    type: DataTypes.STRING
    // allowNull 默认为 true
  }
}, {
  // 这是其他模型参数
});

// `sequelize.define` 会返回模型
console.log(User === sequelize.models.User); // true

在内部,sequelize.define 调用 Model.init,因此两种方法本质上是等效的.

列参数

参考:https://github.com/demopark/sequelize-docs-Zh-CN/blob/master/core-concepts/model-basics.md#uuid

defaultValue

allowNull

primaryKey

autoIncrement

references

Options

如何复用逻辑?

我有 4 张表都是类似的,功能也是类似的,只是储存的数据属性不同,有必要创建 4*3 个文件嘛?

表的继承

可以使用模型作为类

数据类型

参考:https://github.com/demopark/sequelize-docs-Zh-CN/blob/master/core-concepts/model-basics.md

https://sequelize.org/master/variable/index.html#static-variable-DataTypes

要访问内置数据类型,必须导入 DataTypes

const { DataTypes } = require("sequelize"); // 导入内置数据类型\

字符串

DataTypes.STRING             // VARCHAR(255)
DataTypes.STRING(1234)       // VARCHAR(1234)
DataTypes.STRING.BINARY      // VARCHAR BINARY
DataTypes.TEXT               // TEXT
DataTypes.TEXT('tiny')       // TINYTEXT
DataTypes.CITEXT             // CITEXT          仅 PostgreSQL 和 SQLite.

布尔

DataTypes.BOOLEAN            // TINYINT(1)

数字

DataTypes.INTEGER            // INTEGER
DataTypes.BIGINT             // BIGINT
DataTypes.BIGINT(11)         // BIGINT(11)

DataTypes.FLOAT              // FLOAT
DataTypes.FLOAT(11)          // FLOAT(11)
DataTypes.FLOAT(11, 10)      // FLOAT(11,10)

DataTypes.REAL               // REAL            仅 PostgreSQL.
DataTypes.REAL(11)           // REAL(11)        仅 PostgreSQL.
DataTypes.REAL(11, 12)       // REAL(11,12)     仅 PostgreSQL.

DataTypes.DOUBLE             // DOUBLE
DataTypes.DOUBLE(11)         // DOUBLE(11)
DataTypes.DOUBLE(11, 10)     // DOUBLE(11,10)

DataTypes.DECIMAL            // DECIMAL
DataTypes.DECIMAL(10, 2)     // DECIMAL(10,2)

无符号和零填充整数 - 仅限于 MySQL/MariaDB

在 MySQL 和 MariaDB 中,可以将数据类型 INTEGER, BIGINT, FLOATDOUBLE 设置为无符号或零填充 (或两者),如下所示:

DataTypes.INTEGER.UNSIGNED
DataTypes.INTEGER.ZEROFILL
DataTypes.INTEGER.UNSIGNED.ZEROFILL
// 你还可以指定大小,即INTEGER(10)而不是简单的INTEGER
// 同样适用于 BIGINT, FLOAT 和 DOUBLE

日期

DataTypes.DATE       // DATETIME 适用于 mysql / sqlite, 带时区的TIMESTAMP 适用于 postgres
DataTypes.DATE(6)    // DATETIME(6) 适用于 mysql 5.6.4+. 支持6位精度的小数秒
DataTypes.DATEONLY   // 不带时间的 DATE

查询的时候,既可以是 Date 类型,也可以是 YYYY-MM-DD 字符串

UUID

对于 UUID,使用 DataTypes.UUID. 对于 PostgreSQL 和 SQLite,它会是 UUID 数据类型; 对于 MySQL,它则变成 CHAR(36). Sequelize 可以自动为这些字段生成 UUID,只需使用 Sequelize.UUIDV1Sequelize.UUIDV4 作为默认值即可:

{
  type: DataTypes.UUID,
  defaultValue: Sequelize.UUIDV4 // 或 Sequelize.UUIDV1
}

其它

还有其他数据类型,请参见 其它数据类型.

注意

模型同步

定义模型时,你要告诉 Sequelize 有关数据库中表的一些信息. 但是,如果该表实际上不存在于数据库中怎么办? 如果存在,但具有不同的列,较少的列或任何其他差异,该怎么办?

这就是模型同步的来源.可以通过调用一个异步函数 (返回一个 Promise)model.sync(options). 通过此调用,Sequelize 将自动对数据库执行 SQL 查询. 请注意,这仅更改数据库中的表,而不更改 JavaScript 端的模型.

示例:

await User.sync({ force: true });
console.log("用户模型表刚刚(重新)创建!");

模型使用

创建了模型之后可以直接使用增删改查方法

规范

虽然我至今为止都无法理解整个 modules 的意义,但是他确实好用,我也不知道是在哪里看到的了

关联

V5 外键

概述

一对一关系

哲理

示例

参数

onDeleteonUpdate

自定义外键

强制性与可选性关联

一对多关系

哲学

示例

参数

多对多关系

创建、使用 Model 对象

使用 Controller

增删改查(Model 方法)

Model.create()

构建一个新的模型实例,并进行保存。与 build() 方法不同的是,此方法除创建新实例外,还会将其保存到对应数据库表中。

create(values, [options]) -> Promise.<Instance>
/**
 * @param {object} userInfo
 * @return {object}
 */
const createUser = async function(userInfo){
  let res = await UserModel.create(userInfo)
  return res 
}

接受一个对象,对象的属性名与表的列名对应

Finder 方法旨在从数据库查询数据. 他们 返回简单的对象,而是返回模型实例. 因为 finder 方法返回模型实例,你可以按照 实例 的文档中所述,为结果调用任何模型实例成员

通过 get 方法访问实例的值

Model.findByPk()

通过 primary key 查询查询单个实例(单条数据)。

findById(pk, [options]) -> Promise.<Model>
/**
 * @param {number,string,Buffer} pk
 * @return {object}
 */
const getUserByPk = async function(id) {
  const userInfo = await UserModel.findByPk(id)
  return userInfo // 返回数据
}

别名:findByPrimary

findById 已经废弃了

Model.findOne()

查询单个实例(单条数据)。这将会使用 LIMIT 1 查询条件,所以回调中总是返回单个实例。

findOne(options]) -> Promise.<Instance>
/**
 * @param {string} username
 * @return {object}
 */
const getUserByName = async function(username) {
  const userInfo = await User.findOne({
    // 用await控制异步操作,将返回的Promise对象里的数据返回出来。也就实现了“同步”的写法获取异步IO操作的数据
    where: {
      username:username
    }
  })
  return userInfo // 返回数据
}

Model.findAll()

查询多个实例(多条数据)

findAll([options]) -> Promise.<Array.<Instance>>

别名:all

Model.max()

Get the greatest value of a specific attribute within a specific table

查询某个属性的最大值

返回值

异常处理

限制,偏移,顺序和分组

查询最后一个:先 order 倒序,然后 limit 1 个即可

// 返回最新的两个tags
museTags = await MuseModel.findAll({
  order: [['date', 'DESC']],
  limit: 2
})

顺序的话就不用 order 了,直接限制 1 个即可

查询

所有的查询方法都可以通过第二个参数 Option 使用这些规则

属性

想要只选择某些属性,可以使用 attributes 选项. 通常是传递一个数组:

Model.findAll({
  attributes: ['foo', 'bar']
});
SELECT foo, bar ...

属性可以使用嵌套数组来重命名:

Model.findAll({
  attributes: ['foo', ['bar', 'baz']]
});
SELECT foo, bar AS baz ...

也可以使用 sequelize.fn 来进行聚合:

Model.findAll({
  attributes: [[sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']]
});
SELECT COUNT(hats) AS no_hats ...

使用聚合功能时,必须给它一个别名,以便能够从模型中访问它. 在上面的例子中,你可以使用 instance.get('no_hats') 获得帽子数量.

同样,也可以排除某些属性:

Model.findAll({
  attributes: { exclude: ['baz'] }
});

-- Assuming all columns are 'id', 'foo', 'bar', 'baz' and 'qux'
SELECT id, foo, bar, qux FROM ...

Raw

options.raw boolean optional Return raw result. See sequelize.query for more information.

直接返回查询结果,不用再次调用 get() 方法去获取值

Where

https://github.com/demopark/sequelize-docs-Zh-CN/blob/v5/querying.md

操作符

组合范围

const result = await MuseModel.findAll({
  where: {
    date: {
      [Op.gte]: from,
      [Op.lte]: to
    }
  }
})
SELECT * FROM `objects` 
WHERE  (date_field BETWEEN '2010-01-30 14:15:55' AND '2010-09-29 10:15:55')

Model.update()

更新所匹配的多个实例。promise 回调中会返回一个包含一个或两个元素的数组,第一个元素始终表示受影响的行数,第二个元素表示实际影响的行(仅 Postgreoptions.returning 为 true 时受支持)

update(values, options) -> Promise.<Array.<affectedCount, affectedRows>>
/**
 * @param {number} id
 * @param {obeject} newInfo
 * @return {object}
 */
const updateUserById = async function(id,newInfo){
  const userInfo = await UserModel.update(newInfo,{
    where:{
      id:id
    }
  })
  return userInfo
}

Model.destroy()

组合

Model.findOrCreate()

查询与原始查询

https://itbilu.com/nodejs/npm/VJIR1CjMb.html

使用原始查询

FAQ

如何获取表中的最后一个数据?

只能整个表,然后取最后一个