资源:
Sequelize 中文文档
sequelize API
sequelize 小技巧
sequelize 菜鸟教程
核心概念:
连接数据库:
const { Sequelize } = require('sequelize');
// 方法 1: 传递一个连接 URI
const sequelize = new Sequelize('sqlite::memory:') // Sqlite 示例
const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname') // Postgres 示例
// 方法 2: 分别传递参数 (sqlite)
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'path/to/database.sqlite'
});
// 方法 2: 分别传递参数 (其它数据库)
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */
});
模型定义:
sequelize.define('model_name',{filed:value})
创建表:
首先定义模型: model
然后同步:model.sync()
创建表会自动创建主键,默认为 id
增删改查:
-
新增数据
:model.create 相当于 build save两步合并; -
批量新增
:model.bulkCreate([model,...],{...}) ;
但是默认不会运行验证器,需要手动开启
User.bulkCreate([
{ username: 'foo' },
{ username: 'bar', admin: true }
], { validate: true,//手动开启验证器
fields: ['username']//限制字段
});
// 因为限制了字段只存username,foo 和 bar 都不会是管理员.
-
更新
model.update
相当于 set, save两步合并,通常就直接修改实例属性,然后save()更新; -
部分更新
:
通过传递一个列名数组,可以定义在调用 save 时应该保存哪些属性
save({fields:[ 'name',... ]}) 只更新数组里面的字段 -
删除
model.destroy -
重载实例
:model.reload -
查询
:
include参数 对应sql的 join连接操作
findAll 查找所有的
findByPk 根据主键查找
findOne 找到第一个实例
findOrCreate 查找到或创建实例
findAndCountAll 分页查找
查询的选项参数
:
Model.findAll({
//查询指定字段
attributes: ['foo', 'bar',
[sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']//函数聚合
] ,
where: {//对应where子句,过滤
authorId: 2,
authorId: {
[Sequelize.Op.eq]: 2 //操作符运算
}
},
order:[], //排序
group:'name',//分组
limit:10,//限制
offset:1//页
});
实用方法:
count,max, min 和 sum
原始查询:
const { QueryTypes } = require('sequelize');
const users = await sequelize.query("SELECT * FROM `users`");//参数是sql语句
偏执表:
Sequelize 支持 paranoid 表的概念
这意味着删除记录时不会真的删除,而是给字段deletedAt值设置为时间戳
删除的时候默认是软删除,而不是硬删除
class Post extends Model {}
Post.init({ /* 这是属性 */ }, {
sequelize,
paranoid: true,// 传递该参数,创建偏执表
// 如果要为 deletedAt 列指定自定义名称
deletedAt: 'destroyTime'
});
强制删除:
await Post.destroy({
where: {
id: 1
},
force: true //硬删除
});
软删除的实例,恢复:
post.restore();
Post.restore({
where: {
likes: {
[Op.gt]: 100
}
}
});
查询包含软删除的记录:
await Post.findAll({
where: { foo: 'bar' },
paranoid: false
});
关联类型:
对应 sql语句的 foreign key 进行表关联
HasOne BelongsTo HasMany BelongsToMany
const A = sequelize.define('A', /* ... */);
const B = sequelize.define('B', /* ... */);
A.hasOne(B); // A 有一个 B ,外键在目标模型(B)中定义
A.belongsTo(B); // A 属于 B ,外键在目标模型(A)中定义
A.hasMany(B); // A 有多个 B 外键在目标模型(B)中定义
A.belongsToMany(B, { through: 'C' }); // A 属于多个 B , 通过联结表 C
A.belongsToMany(B, { through: 'C' })
关联意味着将表 C
用作联结表,在 A
和 B
之间存在多对多关系. 具有外键(例如,aId
和 bId
). Sequelize 将自动创建此模型 C
(除非已经存在),并在其上定义适当的外键.
创建标准关系:
- 创建一个 一对一 关系,
hasOne
和belongsTo
关联一起使用; - 创建一个 一对多 关系,
hasMany
hebelongsTo
关联一起使用; - 创建一个 多对多 关系, 两个
belongsToMany
调用一起使用.- 注意: 还有一个 超级多对多 关系,一次使用六个关联,将在高级多对多关系指南中进行讨论.
添加到实例的特殊方法:
创建关联关系后,这些模型的实例会获得特殊方法
例如:有两个模型 Foo
和 Bar
拥有关联关系,则根据关联类型拥有以下可用方法;
Foo.hasOne(Bar) 和 Foo.belongsTo(Bar)
#
fooInstance.getBar()
fooInstance.setBar()
fooInstance.createBar()
Foo.hasMany(Bar) 和 Foo.belongsToMany(Bar, { through: Baz })
#
fooInstance.getBars()
fooInstance.countBars()
fooInstance.hasBar()
fooInstance.hasBars()
fooInstance.setBars()
fooInstance.addBar()
fooInstance.addBars()
fooInstance.removeBar()
fooInstance.removeBars()
fooInstance.createBar()
多对多关系:
代码分析:
- 创建表Foo,Bar ,设置为多对多,中间表为Foo_Bar
- sequelize.sync();同步到数据库,就是说如果模型对应的表不存在就创建
插入数据 foo, bar - foo.addBar(bar) ,foo 关联了一个bar,反映到数据库上面,则是中间表Foo_Bar插入一条数据 INSERT INTO Foo_Bar (FooId,BarId) VALUES(1,1)
- Foo.findOne({ include: Bar });数据查询,根据模型,查出Foo表的第一条数据,
并带上关联表数据,字段是Bars(因为是多对多,所以这里是复数形式,每一条bar包含中间表的数据字段是 Foo_Bar
const Foo = sequelize.define('Foo', { name: DataTypes.TEXT });
const Bar = sequelize.define('Bar', { name: DataTypes.TEXT });
Foo.belongsToMany(Bar, { through: 'Foo_Bar' });
Bar.belongsToMany(Foo, { through: 'Foo_Bar' });
await sequelize.sync();
const foo = await Foo.create({ name: 'foo' });
const bar = await Bar.create({ name: 'bar' });
await foo.addBar(bar);// foo这条数据关联了一条bar,反映到表上则是在中间表Foo_Bar上插入一条数据
const fetchedFoo =await Foo.findOne({ include: Bar });
console.log(JSON.stringify(fetchedFoo, null, 2));
输出:
{
"id": 1,
"name": "foo",
"Bars": [
{
"id": 1,
"name": "bar",
"Foo_Bar": {
"FooId": 1,
"BarId": 1
}
}
]
}
高级关联概念
预先加载:
查询方法中使用 include 参数完成预先加载,翻译成sql其实就是 通过join关联子句;
创建关联:
可以一次性创建带关联关系的数据
高级M:N关联:
超级多对多:
超级多对多创建出来的表跟多对多一样,没什么区别,区别就是一次使用6个关联,然后就可以进行各种预先加载
//模型:
const User = sequelize.define('user', {
username: DataTypes.STRING,
points: DataTypes.INTEGER
}, { timestamps: false });
const Profile = sequelize.define('profile', {
name: DataTypes.STRING
}, { timestamps: false });
//自定义中间表
const Grant = sequelize.define('grant', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
selfGranted: DataTypes.BOOLEAN
}, { timestamps: false });
// 超级多对多关系
User.belongsToMany(Profile, { through: Grant });
Profile.belongsToMany(User, { through: Grant });
User.hasMany(Grant);
Grant.belongsTo(User);
Profile.hasMany(Grant);
Grant.belongsTo(Profile);
这样,我们可以进行各种预先加载:
// 全部可以使用:
User.findAll({ include: Profile });
Profile.findAll({ include: User });
User.findAll({ include: Grant });
Profile.findAll({ include: Grant });
Grant.findAll({ include: User });
Grant.findAll({ include: Profile });
多态关联:
多态关联,就是说一个联结表的外键关联多个表
由于外键引用了多个表,无法添加REFERENCES约束,需要禁用约束constraints: false
一对多的多态关联:
考虑模型 Image Video Comment ,
图片,视频都可以有多个评论,
但是一个评论只能是图片跟视频中的其中一种类型;
// Helper 方法
const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`;
class Image extends Model {}
Image.init({
title: DataTypes.STRING,
url: DataTypes.STRING
}, { sequelize, modelName: 'image' });
class Video extends Model {}
Video.init({
title: DataTypes.STRING,
text: DataTypes.STRING
}, { sequelize, modelName: 'video' });
class Comment extends Model {
getCommentable(options) {//获取评论关联类型的那个实例
if (!this.commentableType) return Promise.resolve(null);
const mixinMethodName = `get${uppercaseFirst(this.commentableType)}`;
return this[mixinMethodName](options);
}
}
Comment.init({
title: DataTypes.STRING,
commentableId: DataTypes.INTEGER,
commentableType: DataTypes.STRING
}, { sequelize, modelName: 'comment' });
Image.hasMany(Comment, {
foreignKey: 'commentableId',
constraints: false,
scope: {//关联作用域 commentableType = 'image'
commentableType: 'image'
}
});
Comment.belongsTo(Image, { foreignKey: 'commentableId', constraints: false });
Video.hasMany(Comment, {
foreignKey: 'commentableId',
constraints: false,
scope: {//关联作用域 commentableType = 'video'
commentableType: 'video'
}
});
Comment.belongsTo(Video, { foreignKey: 'commentableId', constraints: false });
Comment.addHook("afterFind", findResult => {
console.log('afterFind,findResult=====',findResult);
if (!Array.isArray(findResult)) findResult = [findResult