zoukankan      html  css  js  c++  java
  • Sequelize-nodejs-10-Hooks

    Hooks钩子

    Hooks (also known as lifecycle events), are functions which are called before and after calls in sequelize are executed. For example, if you want to always set a value on a model before saving it, you can add a beforeUpdate hook.

    钩子(以生命周期事件闻名)是在sequelize的调用被执行的前后被调用的函数。比如,如果你总想要在模型中的值被保存前设置它,你可以添加beforeUpdate钩子

    For a full list of hooks, see Hooks file.

    Order of Operations操作顺序

    (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)

    Declaring Hooks声明

    Arguments to hooks are passed by reference. This means, that you can change the values, and this will be reflected in the insert / update statement. A hook may contain async actions - in this case the hook function should return a promise.

    钩子的变量将通过引用传递。这意味着以可以改变其值,这将会在insert / update语句中被影响。一个钩子可能包含异步行为-在这种情况下,钩子函数应该返回promise。

    There are currently three ways to programmatically add hooks:

    三种添加钩子的方法:

    // Method 1 via the .define() method
    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';
        }
      }
    });
    
    // Method 2 via the .hook() method (or its alias .addHook() method)
    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!"));
    });
    
    // Method 3 via the direct method
    User.beforeCreate((user, options) => {
      return hashPassword(user.password).then(hashedPw => {
        user.password = hashedPw;
      });
    });
    
    User.afterValidate('myHookAfter', (user, options) => {
      user.username = 'Toni';
    });

    Removing hooks移除

    Only a hook with name param can be removed.

    只有带有名字变量的钩子能够被删除

    const Book = sequelize.define('book', {
      title: DataTypes.STRING
    });
    
    Book.addHook('afterCreate', 'notifyUsers', (book, options) => {
      // ...
    });
    
    Book.removeHook('afterCreate', 'notifyUsers');

    You can have many hooks with same name. Calling .removeHook() will remove all of them.

    你可能会有很多有着相同名字的钩子。调用.removeHook()将会删除所有钩子

    Global / universal hooks全局/通用钩子

    Global hooks are hooks which are run for all models. They can define behaviours that you want for all your models, and are especially useful for plugins. They can be defined in two ways, which have slightly different semantics:

    全局钩子是可以在所有模型中运行的。他们能够定义你想要在所有模型中实现的行为,对插件尤其有效。他们可以以两种方式定义,在语义上有所不同。

    Sequelize.options.define (default hook)

    const sequelize = new Sequelize(..., {
        define: {
            hooks: {
                beforeCreate: () => {
                    // Do stuff
                }
            }
        }
    });

    This adds a default hook to all models, which is run if the model does not define its own beforeCreate hook:

    这添加了一个缺省钩子给所有模型,如果模型没有定义它自己的beforeCreate钩子时,这个缺省钩子就会运行

    const User = sequelize.define('user');//没有定义自己的beforeCreate钩子,所以会调用缺省(全局)钩子
    const Project = sequelize.define('project', {}, {//定义了自己的beforeCreate钩子
        hooks: {
            beforeCreate: () => {
                // Do other stuff
            }
        }
    });
    
    User.create() // Runs the global hook
    Project.create() // Runs its own hook (because the global hook is overwritten)

    Sequelize.addHook (permanent hook)

    sequelize.addHook('beforeCreate', () => {
        // Do stuff
    });

    This hooks is always run before create, regardless of whether the model specifies its own beforeCreate hook:

    这个钩子将会在创建前运行,不管一个模型是否定义了它自己的 beforeCreate钩子

    const User = sequelize.define('user');
    const Project = sequelize.define('project', {}, {
        hooks: {
            beforeCreate: () => {
                // Do other stuff
            }
        }
    });
    
    User.create() // Runs the global hook
    Project.create() // Runs its own hook, followed by the global hook,即先运行自己定义的,再运行global的钩子

    Local hooks are always run before global hooks.

    本地钩子将总是运行在全局钩子的前面

    Instance hooks实例钩子

    The following hooks will emit whenever you're editing a single object

    无论你何时去编辑一个对象,下面的钩子将发行:

    beforeValidate
    afterValidate or validationFailed
    beforeCreate / beforeUpdate  / beforeDestroy
    afterCreate / afterUpdate / afterDestroy
    
    // ...define ...需要自己定义
    User.beforeCreate(user => {
      if (user.accessLevel > 10 && user.username !== "Boss") {
        throw new Error("You can't grant this user an access level above 10!")
      }
    })

    This example will return an error:

    下面的例子将返回错误:(即将在create之前运行beforeCreate钩子)

    User.create({username: 'Not a Boss', accessLevel: 20}).catch(err => {
      console.log(err); // You can't grant this user an access level above 10!
    });

    The following example would return successful:

    下面的例子将成功:

    User.create({username: 'Boss', accessLevel: 20}).then(user => {
      console.log(user); // user object with username as Boss and accessLevel of 20
    });

    Model hooks模型钩子

    Sometimes you'll be editing more than one record at a time by utilizing the bulkCreate, update, destroy methods on the model. The following will emit whenever you're using one of those methods:

    有时,你将通过使用模型中的bulkCreate, update, destroy方法去一次编辑多个记录。无论何时你使用这些方法之一的方法时,下面的钩子将会发行

    beforeBulkCreate(instances, options)
    beforeBulkUpdate(options)
    beforeBulkDestroy(options)
    afterBulkCreate(instances, options)
    afterBulkUpdate(options)
    afterBulkDestroy(options)

    If you want to emit hooks for each individual record, along with the bulk hooks you can pass individualHooks: true to the call.

    如果你想要为每个记录发行钩子的话,你可以传递individualHooks: true到bulk钩子中去调用:

    Model.destroy({ where: {accessLevel: 0}, individualHooks: true});
    // Will select all records that are about to be deleted and emit before- + after- Destroy on each instance
    
    Model.update({username: 'Toni'}, { where: {accessLevel: 0}, individualHooks: true});
    // Will select all records that are about to be updated and emit before- + after- Update on each instance

    The options argument of hook method would be the second argument provided to the corresponding method or its cloned and extended version.

    钩子方法的options变量将会成为提供给相关方法或其克隆和扩展版本的第二个变量:

    Model.beforeBulkCreate((records, {fields}) => {
      // records = the first argument sent to .bulkCreate
      // fields = one of the second argument fields sent to .bulkCreate
    })
    
    Model.bulkCreate([
        {username: 'Toni'}, // part of records argument
        {username: 'Tobi'} // part of records argument
      ], {fields: ['username']} // options parameter
    )
    
    Model.beforeBulkUpdate(({attributes, where}) => {
      // where - in one of the fields of the clone of second argument sent to .update
      // attributes - is one of the fields that the clone of second argument of .update would be extended with
    })
    
    Model.update({gender: 'Male'} /*attributes argument*/, { where: {username: 'Tom'}} /*where argument*/)
    
    Model.beforeBulkDestroy(({where, individualHooks}) => {
      // individualHooks - default of overridden value of extended clone of second argument sent to Model.destroy
      // where - in one of the fields of the clone of second argument sent to Model.destroy
    })
    
    Model.destroy({ where: {username: 'Tom'}} /*where argument*/)

    If you use Model.bulkCreate(...) with the updatesOnDuplicate option, changes made in the hook to fields that aren't given in the updatesOnDuplicate array will not be persisted to the database. However it is possible to change the updatesOnDuplicate option inside the hook if this is what you want.

    如果使用带有updatesOnDuplicate选项的Model.bulkCreate(...),在钩子中没有传给updatesOnDuplicate数组的对fields的更改将不会在数据库中保持数据的一致性。可是在钩子中改变updatesOnDuplicate选项是可能的,只要你想。

    // Bulk updating existing users with updatesOnDuplicate option
    Users.bulkCreate([
      { id: 1, isMember: true },
      { id: 2, isMember: false }
    ], {
      updatesOnDuplicate: ['isMember']
    });
    
    User.beforeBulkCreate((users, options) => {
      for (const user of users) {
        if (user.isMember) {
          user.memberSince = new Date();//这里将更改的数据是user中的memberSince,但是它没有在updatesOnDuplicate数组中,所以下面要将其添加进来
        }
      }
    
      // Add memberSince to updatesOnDuplicate otherwise the memberSince date wont be
      // saved to the database
      options.updatesOnDuplicate.push('memberSince');
    });

    Associations

    For the most part hooks will work the same for instances when being associated except a few things

    比如,当被关联的时候,大部分钩子将相同地工作,除了下面的部分:

    1. When using add/set functions the beforeUpdate/afterUpdate hooks will run.当使用beforeUpdate/afterUpdate钩子会运行的add/set函数时
    2. The only way to call beforeDestroy/afterDestroy hooks are on associations with onDelete: 'cascade' and the option hooks: true. For instance:唯一调用beforeUpdate/afterUpdate钩子的方法是与 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);

    This code will run beforeDestroy/afterDestroy on the Tasks table. Sequelize, by default, will try to optimize your queries as much as possible. When calling cascade on delete, Sequelize will simply execute a

    这个代码将在Tasks表中运行beforeDestroy/afterDestroy。默认Sequelize将尽可能地试着优化你的查询。当在删除时调用cascade时,Sequelize将简单地执行:

    DELETE FROM `table` WHERE associatedIdentifier = associatedIdentifier.primaryKey

    However, adding hooks: true explicitly tells Sequelize that optimization is not of your concern and will perform a SELECT on the associated objects and destroy each instance one by one in order to be able to call the hooks with the right parameters.

    可是,显式地添加hooks: true将告诉Sequelize优化器与你所想的不同,它将在关联对象中表现为SELECT和为了能够调用带有正确变量的钩子一个个破坏每个实例。

    If your association is of type n:m, you may be interested in firing hooks on the through model when using the remove call. Internally, sequelize is using Model.destroy resulting in calling the bulkDestroy instead of the before/afterDestroy hooks on each through instance.

    当你使用remove调用时,如果你的关联是类型n:m,你可能将会对在through模型中烧了钩子感兴趣。在内部,sequelize使用Model.destroy导致在每一个through实例中调用bulkDestroy而不是before/afterDestroy钩子

    This can be simply solved by passing {individualHooks: true} to the remove call, resulting on each hook to be called on each removed through instance object.

    这能够简单地通过传递{individualHooks: true}给remove调用来解决,这将导致在每个移除的through实例对象中的每个钩子被调用

    A Note About Transactions

    Note that many model operations in Sequelize allow you to specify a transaction in the options parameter of the method. If a transaction is specified in the original call, it will be present in the options parameter passed to the hook function. For example, consider the following snippet:

    记住很多在Sequelize中的模型操作允许你在方法的options变量中指定事务。如果事务在初始调用中被指定,它将在传递给钩子函数的options变量中出现。比如下面的小片段:

    // Here we use the promise-style of async hooks rather than
    // the callback.
    User.hook('afterCreate', (user, options) => {
      // 'transaction' will be available in options.transaction
    
      // This operation will be part of the same transaction as the
      // original User.create call.
      return User.update({
        mood: 'sad'
      }, {
        where: {
          id: user.id
        },
        transaction: options.transaction
      });
    });
    
    
    sequelize.transaction(transaction => {
      User.create({
        username: 'someguy',
        mood: 'happy',
        transaction
      });
    });

    If we had not included the transaction option in our call to User.update in the preceding code, no change would have occurred, since our newly created user does not exist in the database until the pending transaction has been committed.

    在之前的代码中,如果你对User.update的调用中没有包含事务选项,将不会发生任何变化,因此我们新的创建用户将不会在数据库中出现,知道正在运行中的食物被提交

    Internal Transactions内部事务

    It is very important to recognize that sequelize may make use of transactions internally for certain operations such as Model.findOrCreate. If your hook functions execute read or write operations that rely on the object's presence in the database, or modify the object's stored values like the example in the preceding section, you should always specify { transaction: options.transaction }.

    意识到sequelize内部可能会为某些操作,比如Model.findOrCreate使用事务是十分重要的。如果你的钩子函数依据数据库中出现的对象执行读或写操作,或就像在前面部分的例子中修改对象的存储值,你应该总是指定{ transaction: options.transaction }

    If the hook has been called in the process of a transacted operation, this makes sure that your dependent read/write is a part of that same transaction. If the hook is not transacted, you have simply specified { transaction: null } and can expect the default behaviour.

    如果在事务操作的过程中调用了钩子,要保证你依赖的read/write操作是同一事务的一部分。如果钩子不是事务性的,你可以简单指明{ transaction: null }并期待缺省行为

  • 相关阅读:
    用JS实现汉字转拼音
    jQuery Validate验证框架详解
    移动前端自适应适配布局解决方案和比较
    js获取当前日期时间“yyyy-MM-dd HH:MM:SS”
    jQuery cookie
    dataTable 从服务器获取数据源的两种表现形式
    dataTable 参数说明
    如何在HTML网页中显示HTML标签内容?
    java中构造函数前用public修饰与没有任何修饰符相比,有什么区别?
    面向对象设计
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/10069722.html
Copyright © 2011-2022 走看看