目录
1. 简介
Sequelize 是一个基于 promise 的 Node.js ORM(对象关系映射器),支持多种数据库,包括 Postgres、MySQL、MariaDB、SQLite 以及 Microsoft SQL Server。它提供了强大的事务支持、关联关系、预读和延迟加载、读取复制等功能。
2. 安装
Sequelize 的使用可以通过 npm (或 yarn).
npm install --save sequelize
你还必须手动为所选数据库安装驱动程序:
3. 连接到数据库
要连接到数据库,需要创建一个 Sequelize 实例。这可以通过传递连接参数到 Sequelize 构造函数,或通过传递一个连接 URI 来完成:
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: 分别传递参数
const { Sequelize } = require("sequelize");
const {
MYSQL_HOST,
MYSQL_PORT,
MYSQL_USER,
MYSQL_PWD,
MYSQL_DB,
} = require("../config/config.default");
// 创建数据库实例(数据库名, 用户名, 密码, 配置)
const seq = new Sequelize(MYSQL_DB, MYSQL_USER, MYSQL_PWD, {
host: MYSQL_HOST, // 数据库地址
dialect: "mysql", // 数据库类型
});
Sequelize 构造函数接受许多参数,具体可以查看 API 参考。
4. 测试连接
使用 .authenticate()
方法测试连接是否成功:
// 测试数据库连接;
seq
.authenticate()
.then(() => {
console.log(MYSQL_DB, MYSQL_USER, MYSQL_PWD, MYSQL_HOST, "数据库连接成功");
})
.catch((err) => {
console.log("数据库连接失败", err);
});
5. 关闭连接
默认情况下,Sequelize 会保持连接打开状态。如果需要关闭连接,可以调用 sequelize.close()
方法。注意,一旦调用 sequelize.close()
,就无法再打开新的连接,需要创建一个新的 Sequelize 实例来重新访问数据库。
6. 模型(Model)
模型是 Sequelize 中的一个核心概念,它定义了数据库中的表结构。以下是如何定义一个模型的示例:
define.sync({ force: true });
在 Sequelize 中,.define
方法用于定义模型,而 .sync
方法用于将模型同步到数据库。.sync({ force: true })
是 .sync
方法的一个选项,它的作用是强制同步模型到数据库。这意味着如果数据库中已经存在同名的表,Sequelize 将会删除该表,并根据模型的定义重新创建。这会导致表中所有数据的丢失,因此这个操作是非常危险的,通常只在开发环境中使用,或者在完全了解可能的后果时使用。
belongsTo
belongsTo
是 Sequelize 中定义模型关联关系的一个方法,用于表示一个模型属于另一个模型。例如,如果一个博客文章(Post)属于一个用户(User),则可以使用 belongsTo
在 Post 模型中定义这种关系。这样,每个 Post 实例都会与一个 User 实例相关联,并且可以通过这个关系访问 User 的信息。
在 Sequelize 中,使用 belongsTo
时,会自动在关联的模型中创建一个外键,这个外键指向被关联模型的主键。例如,如果 Post 模型 belongsTo
User 模型,Sequelize 会在 Post 表中创建一个 userId
字段,这个字段是 User 表的主键的外键。
async getCartList(pageNum, pageSize) {
// 分页
const { count, rows } = await Cart.findAndCountAll({
attributes: ["id", "number", "selected"], // 只返回这些字段
offset: (pageNum - 1) * pageSize, // 偏移量
limit: pageSize * 1, // 每页数量
include: {
model: Goods, // 关联 Goods 模型
attributes: ["id", "goods_name", "goods_price", "goods_img"], // 只返回这些字段
as: "goods_info", // 别名
}, // 关联查询 关联 商品 模型
});
return {
pageNum,
pageSize,
total: count,
list: rows,
};
}
paranoid
paranoid
是 Sequelize 提供的一个特性,用于实现软删除。当一个模型被定义为 paranoid
时,调用 destroy
方法并不会真正从数据库中删除记录,而是在记录上设置一个 deletedAt
时间戳字段,标记这条记录为“已删除”。这样,除非特别指定,否则后续的查询都会自动忽略这些被标记为删除的记录。
要启用 paranoid
特性,需要在模型定义时传递 paranoid: true
选项。如果需要,还可以自定义 deletedAt
字段的名称。当需要执行硬删除时,可以通过传递 force: true
选项来覆盖 paranoid
特性,直接从数据库中删除记录。
// cart 购物车表里有商品id 关联 goods 表 id
const { DataTypes } = require("sequelize");
const Goods = require("./goods.model.js");
const seq = require("../db/seq");
// define; 定义模型
const Cart = seq.define("st_cart", {
goods_id: {
type: DataTypes.INTEGER, //INTEGER 整数类型
allowNull: false, // allowNull 是否允许为空
comment: "商品ID", // comment 注释
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: "用户ID",
},
number: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 1,
comment: "商品数量",
},
selected: {
type: DataTypes.BOOLEAN, // 布尔类型
allowNull: false,
defaultValue: true, // defaultValue 默认值
comment: "是否选中",
},
});
// Cart.sync({ force: true });
/**
* 关联关系
* Cart表中有一个goods_id字段
* 这个goods_id引用了Goods表的id
* 每个购物车项只能对应一个商品(belongsTo)
* 外键的作用
* 数据完整性:确保购物车中的商品一定存在于商品表中
* 关联查询:可以方便地查询商品的详细信息
*/
// 关联 Goods 模型 使用 belongsTo 方法 关联外键 goods_id 并设置别名 goods_info
Cart.belongsTo(Goods, { foreignKey: "goods_id", as: "goods_info" }); // 因为goods_id 是外键,所以需要使用 belongsTo 关联 Goods 模型
module.exports = Cart;
7. 增删改查(CRUD)
7.1 查询(Query)
使用模型的 .findAll()
方法进行查询:
- attributes 指定返回的字段
- where 查询条件
async getAddrList(user_id) {
return await Address.findAll({
attributes: ["id", "consignee", "phone", "address", "is_default"], // 指定返回的字段
where: { user_id },
});
}
7.2 插入(Create)
使用模型的 .create()
方法插入数据:、
User.create({
username: 'newuser',
email: '[email protected]'
}).then(user => {
console.log(user);
});
7.3 更新(Update)
使用模型的 .update()
方法更新数据:
User.update({
username: 'updateduser'
}, {
where: {
id: 1
}
}).then(result => {
console.log(result);
});
7.4 删除(Delete)
使用模型的 .destroy()
方法删除数据:
force: true, // 添加 force: true 实现硬删除
async removeGoods(id) {
const res = await Goods.destroy({
where: { id },
force: true, // 添加 force: true 实现硬删除
});
console.log(res);
return res > 0 ? true : false;
}
7.5 软删除 (restore
)
在 Sequelize 中,restore
方法用于恢复被软删除的记录。当模型启用了 paranoid
模式后,记录在被 destroy
方法调用时并不会真正从数据库中删除,而是标记为已删除。使用 restore
方法可以将这些被标记为已删除的记录恢复到可查询的状态。
// 假设有一个启用了 paranoid 的模型实例 instance
instance.restore()
.then(() => {
// 记录已恢复
})
.catch(error => {
// 处理错误
});
7.6 分页 (findAndCountAll
)
findAndCountAll
方法用于执行查询并返回两个值:匹配查询条件的记录集合和这些记录的总数。这对于分页显示结果非常有用。
async getGoodsList(pageNum, pageSize) {
// offset 偏移量 (当前页码-1)* 每页条数 = 跳过多少条数据
const offset = (pageNum - 1) * pageSize;
// 获取商品总数 (全局开启 paranoid: true 软删除之后count会自动过滤掉)
// const count = await Goods.count();
// 获取商品列表
// const rows = await Goods.findAll({
// offset, // 跳过条数
// limit: Number(pageSize), // 获取条数
// });
// 获取商品总数和商品列表 可以减少数据库查询次数
const { count, rows } = await Goods.findAndCountAll({
offset, // 跳过条数
limit: Number(pageSize), // 获取条数
});
return {
pageNum,
pageSize,
total: count,
list: rows,
};
}
7.7 查找单条(findOne)
findOne
方法用于查找符合特定条件的第一个记录。如果找到了符合条件的记录,它将返回一个模型实例;如果没有找到,将返回 null
。
Model.findOne({
where: {
// 查询条件
}
})
.then(instance => {
if (instance) {
// 找到了符合条件的记录
} else {
// 没有找到符合条件的记录
}
})
.catch(error => {
// 处理错误
});
7.8 主键查询 (findByPk
)
findByPk
(Find By Primary Key)是 Sequelize 提供的一个方法,用于根据主键值查找记录。这个方法非常适合当你知道要查询记录的具体主键值时使用。它直接查询数据库,返回与给定主键值相匹配的单个记录。
// 假设有一个模型实例 Model 和一个主键值 pk
Model.findByPk(pk)
.then(instance => {
if (instance) {
// 找到了主键值匹配的记录
console.log(instance);
} else {
// 没有找到匹配的记录
console.log('No instance found');
}
})
.catch(error => {
// 处理错误
console.error(error);
});
8. 实例方法
在 Sequelize 中,increment
、reload
和 save
是实例方法,它们可以在模型的单个实例上调用,用于执行特定的数据库操作。下面是这些方法的详细说明和使用示例:
increment
increment
方法用于将实例的某个字段的值增加一个指定的数值。这个方法不会影响其他字段,并且会自动处理字段值的数据类型转换。如果字段值是整数,那么增加的数值可以是任何整数;如果字段值是浮点数,那么增加的数值可以是小数。
// 假设有一个 User 模型实例 user
user.increment('age', { by: 1 }) // 将 age 字段增加 1
.then(() => {
// 增加操作成功,user 对象的 age 属性已经更新
console.log(user.age); // 输出更新后的 age 值
})
.catch(error => {
// 处理错误
console.error(error);
});
increment
方法接受两个参数:第一个是要增加的字段名,第二个是一个选项对象,其中 by
属性指定了要增加的数值。
reload
reload
方法用于重新从数据库中加载实例的数据。这在你更新了数据库中的记录,但希望在不重新查询数据库的情况下更新 Sequelize 实例的状态时非常有用。
// 假设有一个 User 模型实例 user
user.reload()
.then(() => {
// 实例已重新加载,user 对象的数据现在是最新的
console.log(user.get()); // 输出重新加载后的用户数据
})
.catch(error => {
// 处理错误
console.error(error);
});
save
save
方法用于将实例的当前状态保存到数据库。如果实例是新的(即还没有被创建),save
方法会执行一个 INSERT
操作;如果实例已经存在(即主键字段有值),save
方法会执行一个 UPDATE
操作。
javascript
// 假设有一个 User 模型实例 user
user.save()
.then(() => {
// 实例已保存到数据库
console.log('User saved successfully');
})
.catch(error => {
// 处理错误
console.error(error);
});
save
方法会检查实例的更改,并且只更新那些被修改过的字段。如果实例没有任何更改,save
方法将不会执行任何数据库操作。
Sequelize 的 Op 对象
Sequelize 的 Op
对象提供了一组用于构建复杂查询条件的操作符。这些操作符可以帮助开发者在查询数据库时更加灵活和高效地使用多个条件进行筛选。
opt.and
opt.and
是一个操作符,用于在查询中组合多个条件,只有当所有条件都满足时才返回结果。它通常与一个数组一起使用,数组中的每个元素都是一个条件对象。这意味着,只有当所有条件都为真时,查询才会返回结果。
const { Op } = require('sequelize');
Model.findAll({
where: {
[Op.and]: [
{ age: { [Op.gt]: 18 } }, // 年龄大于18
{ gender: 'male' } // 性别为男性
]
}
});
在上面的代码中,我们使用了 Op.and
来组合两个条件:年龄大于18岁和性别为男性。只有同时满足这两个条件的记录才会被查询出来。
opt.in
opt.in
是一个操作符,用于查询符合指定数组中任意值的结果。在 where
条件对象中指定相关的属性及具体查询的值,例如:
const { Op } = require('sequelize');
Model.findAll({
where: {
id: {
[Op.in]: [1, 2, 3] // id 为 1、2 或 3
}
}
});
上面的操作会查询 Model
表中所有 id
值为 1、2、3 的结果集。opt.in
非常适合用于检查某个字段的值是否在给定的数组中。
通过使用 Op
对象中的 and
和 in
操作符,Sequelize 使得构建复杂查询变得更加简单和直观。这些操作符在实际开发中非常有用,尤其是在需要根据多个条件进行数据筛选时。