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 }並期待缺省行為


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM