写在前面
大家好,我是一溪风月🥸,一名前端工程师,专注于前端,Node,Golang的知识分享,在最近这段时间在学习Node服务端开发的知识,学习服务端必然少不了数据库,少不了SQL语言,但是其实现代服务端开发中几乎不直接编写语句操作SQL了,大家都会选择一个叫做ORM的技术来实现原本庞杂的任务,结合Express和Koa最推荐的ORM库就是Sequlize了,今天我们就来开启这个库的学习,学习资料为官方文档,有兴趣可以直接去查看官方文档,当然这篇文章会尽量将我在学习中的疑惑讲清楚,弄明白,可能我的疑惑也是你的疑惑,好了,那让我们开始吧!
一.配置学习环境
首先我们来配置一下Sequlize所需要的环境,相信你已经安装了Node,我们直接跳过这一步来使用npm进行初始化
js
npm init -y
然后我们进行Sequelize的安装
js
npm install --save sequelize
这里的数据库我们选择MySQL,我们首选在数据库管理工具中新建一个数据库,我是用的是Navicat。
二.连接数据库
首先我们需要先在项目中新建一个文件用来引入Sequelize,进行数据库的连接,官方推荐了三种数据库的连接方式
js
const { Sequelize } = require('sequelize');
// 方法 1: 传递一个连接 URI
const sequelize = new Sequelize('sqlite::memory:') // Sqlite 示例
const sequelize = new Sequelize('postgres://user:[email protected]:5432/dbname') // Postgres 示例
// 方法 2: 分别传递参数 (sqlite)
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'path/to/database.sqlite'
});
// 方法 3: 分别传递参数 (其它数据库)
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: /* one of 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mssql' | 'db2' | 'snowflake' | 'oracle' */
});
我们选择最后一种,因为我觉的最后一种比较理解,并且我们使用的是MySQL,第二种是专门给Node内置的数据SQLite准备的。
js
const Sequelize = require("sequelize")
const sequelize = new Sequelize('sequlize_test', 'root', 'root', {
host: 'localhost',
dialect: 'mysql'
})
然后我们编写一个函数进行连接测试,看是否和我们本地的MySQL连接成功了,完整的代码就是这样。
js
const Sequelize = require("sequelize")
const sequelize = new Sequelize('sequelize_test', 'root', 'root', {
host: 'localhost',
dialect: 'mysql'
})
// 测试连接
async function testConnecton () {
try {
await sequelize.authenticate()
console.log('Connection has been established successfully.')
} catch (error) {
console.error('Unable to connect to the database:', error)
}
}
testConnecton();
执行一下node index.js
我们会看到了数据库成功连接了
其次Sequelize还提供了关闭连接的方法,如果我们执行一次短期脚本后就不用数据库了可以使用如下的方法继续宁关闭数据库的连接,但是我们大多数服务都是长期连接的就不需要这个方法
js
sequelize.close()
💡官方提示:一旦
sequelize.close()
被调用, 就不可能打开新的连接. 你将需要创建一个新的 Sequelize 实例以再次访问你的数据库。
三.记录日志
日志顾名思义就是在某个时间干了某件事,但是为什么要记录日志? 当然是为了在某些时间服务出了问题进行数据的排查,在Sequelize中我们可以这样来启用日志
js
// 选择一种日志记录参数
logging: console.log, // 默认值,显示日志函数调用的第一个参数
logging: (...msg) => console.log(msg), // 显示所有日志函数调用参数
logging: false, // 禁用日志记录
logging: msg => logger.debug(msg), // 使用自定义记录器(例如Winston 或 Bunyan),显示第一个参数
logging: logger.debug.bind(logger) // 使用自定义记录器的另一种方法,显示所有消息
以上这几种写法根据需求任选其一,在我们的代码中目前并没有使用日志库来记录,我们就可以这样写
js
const sequelize = new Sequelize('sequelize_test', 'root', 'root', {
host: 'localhost',
dialect: 'mysql',
logging:console.log,
})
三.新数据库和现有数据库如何使用?
想要理解这个问题就要先了解一下什么是ORM,ORM 即对象关系映射(Object Relational Mapping),是一种编程技术,用于在面向对象编程语言(如 Python、Java、JavaScript 等)和关系型数据库(如 MySQL、PostgreSQL、Oracle 等)之间建立起一座桥梁,使得开发者能够以面向对象的方式来操作数据库,简单来说就是这样。
以Sequelize为例,本质上就是和数据库之间构建的一座桥梁,从具体的代码中来看就是类/方法
对应具体数据库中的表,比如图中的User对应的就是数据库中User表的这种映射关系,而类的实例化对象对应的就是表中的一行数据。当我们没有数据库的时候我们需要新建一个数据库,我们就可以通过数据库工具比如Navicat来新建数据库,我们可以直接通过Sequelize来定义模型去创建对应的表,如果数据库中有数据Sequelize依然能够使用。
四.模型基础
模型是ORM中一个很重要的概念,理解了模型就理解了ORM,就像Express中的中间件一样,但是和上述的内容ORM中的User去对应数据库中的User表,但是在Sequelize中对应关系通常是这样的User->Users
也就是说当我们数据库是空的,我们使用Sequelize定义了模型,它自动同步到数据库创建的表就是对应的复数形式,甚至有时候还会有这样的对应关系person->people
是通过官方的一种规范来做转换的,但是我感觉挺反人类的,建议在定义模型的时候使用下面这种方式关掉。
js
sequelize.define('User', {
// ... (属性)
}, {
freezeTableName: true
});
当然也可以全局进行关闭,完整的代码就是这样。
js
const Sequelize = require("sequelize")
const sequelize = new Sequelize('sequelize_test', 'root', 'root', {
host: 'localhost',
dialect: 'mysql',
logging: console.log,
define: {
freezeTableName: true
}
})
当然我们也可以直接提供表名就相当于给表起了别名,其实这样更加方便。
js
sequelize.define('User', {
// ... (属性)
}, {
tableName: 'Employees'
});
那么现在我们来新建一个模型,我们直接使用更加简洁的define
方法来进行定义。
js
const User = sequelize.define('User', {
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING,
defaultValue:"志鹏"
}
})
还有其他的模型创建方式可以去查看中文文档 模型基础 之后我们需要进行模型的同步才能在数据库中建立真实的表,对模型的同步有如下的几种:
User.sync()
- 如果表不存在,则创建该表(如果已经存在,则不执行任何操作)User.sync({ force: true })
- 将创建表,如果表已经存在,则将其首先删除User.sync({ alter: true })
- 这将检查数据库中表的当前状态(它具有哪些列,它们的数据类型等),然后在表中进行必要的更改以使其与模型匹配.
我们因为是直接是通过Sequelize来创建的,所以我们使用第一种来进行同步。
js
// 进行模型同步
async function modelSync () {
await User.sync()
}
modelSync()
然后我们来执行一下代码,我们会发现上面的内容打印也生效了
通过数据库工具中查看也新增了User表。
我们也可以直接全局进行配置,来一次性同步全部模型
js
await sequelize.sync({ force: true });
console.log("所有模型均已成功同步.");
但是你可能会发现一个问题,为什么我们没有定义createdAt
和updatedAt
但是在数据库表中会自动增加创建时间和更新时间?这个是Sequelize默认的配置,我们如果不想要这两个字段也可以按照如下配置。
js
// 不要忘记启用时间戳!
timestamps: true,
// 不想要 createdAt
createdAt: false,
// 想要 updatedAt 但是希望名称叫做 updateTimestamp
updatedAt: 'updateTimestamp'
五.数据库安全检查
除了上面的同步表数据,还可以进行数据库表的删除,也可以删除全部表
js
// 删除单个表
await User.drop();
console.log("用户表已删除!");
// 删除全部表
await sequelize.drop();
console.log("所有表已删除!");
因为前面我们可以通过async同时设置设置force属性来强制同步表,并且也可以进行删除表,这在实际开发中是非常危险的,所以为了防止误删等情况我们往往需要进行数据库检查我们可以用match
来进行正则匹配某个具体的数据库名称,当匹配成功就执行删除或者,同步,用法如下
js
// 仅当数据库名称以 '_test' 结尾时,它才会运行.sync()
sequelize.sync({ force: true, match: /_test$/ });
六.数据类型
我们在上述内容中定义了具体模型的字段,并且给不同的模型定义了不同的字段以及具体的类型,类型是通过导入如下的方式进行使用
js
const { DataTypes } = require("sequelize"); // 导入内置数据类型
字符串
js
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.TSVECTOR // TSVECTOR 仅 PostgreSQL.
开发中对于姓名等固定的字符串我们可以使用STRING
对于更多的文字我们可以使用TEXT
布尔
js
DataTypes.BOOLEAN // TINYINT(1)
数字
js
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
, FLOAT
和 DOUBLE
设置为无符号或零填充(或两者),如下所示:
js
DataTypes.INTEGER.UNSIGNED
DataTypes.INTEGER.ZEROFILL
DataTypes.INTEGER.UNSIGNED.ZEROFILL
// 你还可以指定大小,即INTEGER(10)而不是简单的INTEGER
// 同样适用于 BIGINT, FLOAT 和 DOUBLE
日期
js
DataTypes.DATE // DATETIME 适用于 mysql / sqlite, 带时区的TIMESTAMP 适用于 postgres
DataTypes.DATE(6) // DATETIME(6) 适用于 mysql 5.6.4+. 支持6位精度的小数秒
DataTypes.DATEONLY // 不带时间的 DATE
UUID
对于 UUID,使用 DataTypes.UUID
. 对于 PostgreSQL 和 SQLite,它会是 UUID
数据类型;对于 MySQL,它则变成CHAR(36)
. Sequelize 可以自动为这些字段生成 UUID,只需使用 DataTypes.UUIDV1
或 DataTypes.UUIDV4
作为默认值即可:
js
{
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4 // 或 DataTypes.UUIDV1
}
常见列参数
js
// 实例化将自动将 flag 设置为 true (如果未设置)
flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
// 日期的默认值 => 当前时间
myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
// 将 allowNull 设置为 false 将为该列添加 NOT NULL,
// 这意味着如果该列为 null,则在执行查询时将从数据库引发错误.
// 如果要在查询数据库之前检查值是否不为 null,请查看下面的验证部分.
title: { type: DataTypes.STRING, allowNull: false },
// 创建两个具有相同值的对象将引发错误.
// unique 属性可以是布尔值或字符串.
// 如果为多个列提供相同的字符串,则它们将形成一个复合唯一键.
uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },
// unique 属性是创建唯一约束的简写.
someUnique: { type: DataTypes.STRING, unique: true },
// 继续阅读有关主键的更多信息
identifier: { type: DataTypes.STRING, primaryKey: true },
// autoIncrement 可用于创建 auto_incrementing 整数列
incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },
// 你可以通过 'field' 属性指定自定义列名称:
fieldWithUnderscores: { type: DataTypes.STRING, field: 'field_with_underscores' },
// 可以创建外键:
bar_id: {
type: DataTypes.INTEGER,
references: {
// 这是对另一个模型的参考
model: Bar,
// 这是引用模型的列名
key: 'id',
// 使用 PostgreSQL,可以通过 Deferrable 类型声明何时检查外键约束.
deferrable: Deferrable.INITIALLY_IMMEDIATE
// 参数:
// - `Deferrable.INITIALLY_IMMEDIATE` - 立即检查外键约束
// - `Deferrable.INITIALLY_DEFERRED` - 将所有外键约束检查推迟到事务结束
// - `Deferrable.NOT` - 完全不推迟检查(默认) - 这将不允许你动态更改事务中的规则
}
},
// 注释只能添加到 MySQL,MariaDB,PostgreSQL 和 MSSQL 的列中
commentMe: {
type: DataTypes.INTEGER,
comment: '这是带有注释的列'
}
}, {
sequelize,
modelName: 'foo',
// 在上面的属性中使用 `unique: true` 与在模型的参数中创建索引完全相同:
indexes: [{ unique: true, fields: ['someUnique'] }]
七.总结与扩展
这篇文章到这里暂时就结束了,这篇文章我们认识了Sequelize这个数据库ORM,上述的内容主要来源本人通过官方文档的学习和理解,建议参考着官方文档进行学习:模型基础 在上述我们是通过define来映射表,其实我们也可以通过继承来实现模型创建,这两种方式是一样的,具体详情请参考文档,如下是本章节的的所有的代码
js
const { Sequelize, DataTypes } = require("sequelize");
// 配置数据库连接
const sequelize = new Sequelize('sequelize_test', 'root', 'root', {
host: 'localhost',
dialect: 'mysql',
// 可根据需要调整日志级别,生产环境可设置为 false 以减少日志输出
logging: console.log,
define: {
freezeTableName: true
}
});
// 定义 User 模型
const User = sequelize.define('User', {
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING
defaultValue:"zzz"
}
});
// 测试数据库连接
async function testConnection() {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
return true;
} catch (error) {
console.error('Unable to connect to the database:', error);
return false;
}
}
// 进行模型同步
async function syncModels() {
try {
await User.sync();
console.log('Models synchronized successfully.');
} catch (error) {
console.error('Error synchronizing models:', error);
}
}
// 主函数,按顺序执行操作
async function main() {
const isConnected = await testConnection();
if (isConnected) {
await syncModels();
}
}
// 调用主函数
main();
下一篇文章我们将讲解模型实例(映射到真实数据中就是具体某一行的数据)下篇文章见🚀~