此系列文章的应用示例已发布于 GitHub: sequelize-docs-Zh-CN. 能够 Fork 帮助改进或 Star 关注更新. 欢迎 Star.git
Hook(也称为生命周期事件)是执行 sequelize 调用以前和以后调用的函数。 例如,若是要在保存模型以前始终设置值,能够添加一个 beforeUpdate
hook。github
获取完整列表, 请查看 Hooks file.sql
(1) beforeBulkCreate(instances, options) beforeBulkDestroy(options) beforeBulkUpdate(options) (2) beforeValidate(instance, options) (-) validate (3) afterValidate(instance, options) - or - validationFailed(instance, options, error) (4) beforeCreate(instance, options) beforeDestroy(instance, options) beforeUpdate(instance, options) beforeSave(instance, options) beforeUpsert(values, options) (-) create destroy update (5) afterCreate(instance, options) afterDestroy(instance, options) afterUpdate(instance, options) afterSave(instance, options) afterUpsert(created, options) (6) afterBulkCreate(instances, options) afterBulkDestroy(options) afterBulkUpdate(options)
Hook 的参数经过引用传递。 这意味着您能够更改值,这将反映在insert / update语句中。 Hook 可能包含异步动做 - 在这种状况下,Hook 函数应该返回一个 promise。数据库
目前有三种以编程方式添加 hook 的方法:编程
// 方法1 经过 .define() 方法 const User = sequelize.define('user', { username: DataTypes.STRING, mood: { type: DataTypes.ENUM, values: ['happy', 'sad', 'neutral'] } }, { hooks: { beforeValidate: (user, options) => { user.mood = 'happy'; }, afterValidate: (user, options) => { user.username = 'Toni'; } } }); // 方法2 经过 . hook() 方法 (或其别名 .addHook() 方法) User.hook('beforeValidate', (user, options) => { user.mood = 'happy'; }); User.addHook('afterValidate', 'someCustomName', (user, options) => { return sequelize.Promise.reject(new Error("I'm afraid I can't let you do that!")); }); // 方法3 经过直接方法 User.beforeCreate((user, options) => { return hashPassword(user.password).then(hashedPw => { user.password = hashedPw; }); }); User.afterValidate('myHookAfter', (user, options) => { user.username = 'Toni'; });
只能删除有名称参数的 hook。数组
const Book = sequelize.define('book', { title: DataTypes.STRING }); Book.addHook('afterCreate', 'notifyUsers', (book, options) => { // ... }); Book.removeHook('afterCreate', 'notifyUsers');
你能够有不少同名的 hook。 调用 .removeHook()
将会删除它们。promise
全局 hook 是全部模型的 hook。 他们能够定义您想要的全部模型的行为,而且对插件特别有用。 它们能够用两种方式来定义,它们的语义略有不一样:app
const sequelize = new Sequelize(..., { define: { hooks: { beforeCreate: () => { // 作些什么 } } } });
这将为全部模型添加一个默认 hook,若是模型没有定义本身的 beforeCreate
hook,那么它将运行。异步
const User = sequelize.define('user'); const Project = sequelize.define('project', {}, { hooks: { beforeCreate: () => { // 作些其它什么 } } }); User.create() // 运行全局 hook Project.create() // 运行其自身的 hook (由于全局 hook 被覆盖)
sequelize.addHook('beforeCreate', () => { // 作些什么 });
这个 hook 老是在建立以前运行,不管模型是否指定了本身的 beforeCreate
hook:函数
const User = sequelize.define('user'); const Project = sequelize.define('project', {}, { hooks: { beforeCreate: () => { // 作些其它什么 } } }); User.create() // 运行全局 hook Project.create() //运行其本身的 hook 以后运行全局 hook
本地 hook 老是在全局 hook 以前运行。
当您编辑单个对象时,如下 hook 将触发
beforeValidate afterValidate or validationFailed beforeCreate / beforeUpdate / beforeDestroy afterCreate / afterUpdate / afterDestroy
// ...定义 ... User.beforeCreate(user => { if (user.accessLevel > 10 && user.username !== "Boss") { throw new Error("您不能授予该用户10级以上的访问级别!") } })
此示例将返回错误:
User.create({username: 'Not a Boss', accessLevel: 20}).catch(err => { console.log(err); // 您不能授予该用户 10 级以上的访问级别! });
如下示例将返回成功:
User.create({username: 'Boss', accessLevel: 20}).then(user => { console.log(user); // 用户名为 Boss 和 accessLevel 为 20 的用户对象 });
有时,您将一次编辑多个记录,方法是使用模型上的 bulkCreate, update, destroy
方法。 当您使用如下方法之一时,将会触发如下内容:
beforeBulkCreate(instances, options) beforeBulkUpdate(options) beforeBulkDestroy(options) afterBulkCreate(instances, options) afterBulkUpdate(options) afterBulkDestroy(options)
若是要为每一个单独的记录触发 hook,连同批量 hook,您能够将 personalHooks:true
传递给调用。
Model.destroy({ where: {accessLevel: 0}, individualHooks: true}); // 将选择要删除的全部记录,并在每一个实例删除以前 + 以后触发 Model.update({username: 'Toni'}, { where: {accessLevel: 0}, individualHooks: true}); // 将选择要更新的全部记录,并在每一个实例更新以前 + 以后触发
Hook 方法的 options
参数将是提供给相应方法或其克隆和扩展版本的第二个参数。
Model.beforeBulkCreate((records, {fields}) => { // records = 第一个参数发送到 .bulkCreate // fields = 第二个参数字段之一发送到 .bulkCreate }) Model.bulkCreate([ {username: 'Toni'}, // 部分记录参数 {username: 'Tobi'} // 部分记录参数 ], {fields: ['username']} // 选项参数 ) Model.beforeBulkUpdate(({attributes, where}) => { // where - 第二个参数的克隆的字段之一发送到 .update // attributes - .update 的第二个参数的克隆的字段之一被用于扩展 }) Model.update({gender: 'Male'} /*属性参数*/, { where: {username: 'Tom'}} /*where 参数*/) Model.beforeBulkDestroy(({where, individualHooks}) => { // individualHooks - 第二个参数被扩展的克隆被覆盖的默认值发送到 Model.destroy // where - 第二个参数的克隆的字段之一发送到 Model.destroy }) Model.destroy({ where: {username: 'Tom'}} /*where 参数*/)
若是用 updates.OnDuplicate
参数使用 Model.bulkCreate(...)
,那么 hook 中对 updatesOnDuplicate
数组中没有给出的字段所作的更改将不会被持久保留到数据库。 可是,若是这是您想要的,则能够更改 hook 中的 updatesOnDuplicate 选项。
// 使用 updatesOnDuplicate 选项批量更新现有用户 Users.bulkCreate([ { id: 1, isMemeber: true }, { id: 2, isMember: false } ], { updatesOnDuplicate: ['isMember'] }); User.beforeBulkCreate((users, options) => { for (const user of users) { if (user.isMember) { user.memberSince = new Date(); } } // 添加 memberSince 到 updatesOnDuplicate 不然 memberSince 期将不会被保存到数据库 options.updatesOnDuplicate.push('memberSince'); });
在大多数状况下,hook 对于相关联的实例而言将是同样的,除了几件事情以外。
onDelete:'cascade
和参数 hooks:true
相关联。 例如:const Projects = sequelize.define('projects', { title: DataTypes.STRING }); const Tasks = sequelize.define('tasks', { title: DataTypes.STRING }); Projects.hasMany(Tasks, { onDelete: 'cascade', hooks: true }); Tasks.belongsTo(Projects);
该代码将在Tasks表上运行beforeDestroy / afterDestroy。 默认状况下,Sequelize会尝试尽量优化您的查询。 在删除时调用级联,Sequelize将简单地执行一个
DELETE FROM `table` WHERE associatedIdentifier = associatedIdentifier.primaryKey
然而,添加 hooks: true
会明确告诉 Sequelize,优化不是你所关心的,而且会在关联的对象上执行一个 SELECT
,并逐个删除每一个实例,以便可以使用正确的参数调用 hook。
若是您的关联类型为 n:m
,则在使用 remove
调用时,您可能有兴趣在直通模型上触发 hook。 在内部,sequelize 使用 Model.destroy
,导致在每一个实例上调用 bulkDestroy
而不是 before / afterDestroy
hook。
这能够经过将 {individualHooks:true}
传递给 remove
调用来简单地解决,从而致使每一个 hook 都经过实例对象被删除。
请注意,Sequelize 中的许多模型操做容许您在方法的 options 参数中指定事务。 若是在原始调用中 指定 了一个事务,它将出如今传递给 hook 函数的 options 参数中。 例如,请参考如下代码段:
// 这里咱们使用异步 hook 的 promise 风格,而不是回调。 User.hook('afterCreate', (user, options) => { // 'transaction' 将在 options.transaction 中可用 // 此操做将成为与原始 User.create 调用相同的事务的一部分。 return User.update({ mood: 'sad' }, { where: { id: user.id }, transaction: options.transaction }); }); sequelize.transaction(transaction => { User.create({ username: 'someguy', mood: 'happy', transaction }); });
若是咱们在上述代码中的 User.update
调用中未包含事务选项,则不会发生任何更改,由于在已提交挂起的事务以前,咱们新建立的用户不存在于数据库中。
要认识到 sequelize 可能会在某些操做(如 Model.findOrCreate
)内部使用事务是很是重要的。 若是你的 hook 函数执行依赖对象在数据库中存在的读取或写入操做,或者修改对象的存储值,就像上一节中的例子同样,你应该老是指定 { transaction: options.transaction }
。
若是在处理操做的过程当中已经调用了该 hook ,则这将确保您的依赖读/写是同一事务的一部分。 若是 hook 没有被处理,你只须要指定{ transaction: null }
而且能够预期默认行为。
若是这篇文章对您有帮助, 感谢 下方点赞 或 Star GitHub: sequelize-docs-Zh-CN 支持, 谢谢.