Sequelize官方GitHub:https://github.com/sequelize/sequelize
Sequelize官方中文文檔:https://github.com/demopark/sequelize-docs-Zh-CN
Sequelize官方英文文檔:http://docs.sequelizejs.com/
Transactions - 事務(wù)
Sequelize 支持兩種使用事務(wù)的方法:
- 一個將根據(jù) promise 鏈的結(jié)果自動提交或回滾事務(wù),(如果啟用)用回調(diào)將該事務(wù)傳遞給所有調(diào)用
- 而另一個 leave committing,回滾并將事務(wù)傳遞給用戶。
主要區(qū)別在于托管事務(wù)使用一個回調(diào),對非托管事務(wù)而言期望 promise 返回一個 promise 的結(jié)果。
托管事務(wù)(auto-callback)
托管事務(wù)自動處理提交或回滾事務(wù)。你可以通過將回調(diào)傳遞給 sequelize.transaction 來啟動托管事務(wù)。
注意回傳傳遞給 transaction 的回調(diào)是否是一個 promise 鏈,并且沒有明確地調(diào)用t.commit()或 t.rollback()。 如果返回鏈中的所有 promise 都已成功解決,則事務(wù)被提交。 如果一個或幾個 promise 被拒絕,事務(wù)將回滾。
return sequelize.transaction(function (t) {
// 在這里鏈接您的所有查詢。 確保你返回他們。
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(function (user) {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {transaction: t});
});
}).then(function (result) {
// 事務(wù)已被提交
// result 是 promise 鏈返回到事務(wù)回調(diào)的結(jié)果
}).catch(function (err) {
// 事務(wù)已被回滾
// err 是拒絕 promise 鏈返回到事務(wù)回調(diào)的錯誤
});
拋出錯誤到回滾
使用托管事務(wù)時,你應(yīng)該 永不 手動提交或回滾事務(wù)。 如果所有查詢都成功,但您仍然希望回滾事務(wù)(例如因為驗證失?。瑒t應(yīng)該拋出一個錯誤來斷開和拒絕鏈接:
return sequelize.transaction(function (t) {
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(function (user) {
// 查詢成功,但我們?nèi)匀幌牖貪L!
throw new Error();
});
});
自動將事務(wù)傳遞給所有查詢
在上面的例子中,事務(wù)仍然是手動傳遞的,通過傳遞 {transaction:t} 作為第二個參數(shù)。 要自動將事務(wù)傳遞給所有查詢,您必須安裝 continuation local storage (CLS) 模塊,并在您自己的代碼中實例化一個命名空間:
const cls = require('continuation-local-storage'),
namespace = cls.createNamespace('my-very-own-namespace');
要啟用CLS,您必須通過使用sequelize構(gòu)造函數(shù)的靜態(tài)方法來告訴Sequelize要使用的命名空間:
const Sequelize = require('sequelize');
Sequelize.useCLS(namespace);
new Sequelize(....);
請注意, useCLS() 方法在 構(gòu)造函數(shù) 上,而不是在 sequelize 的實例上。 這意味著所有實例將共享相同的命名空間,并且 CLS 是全部或全無方式 - 你不能僅在某些實例中啟用它。
CLS 的工作方式就像一個用于回調(diào)的本地線程存儲。 這在實踐中意味著不同的回調(diào)鏈可以通過使用 CLS 命名空間來訪問局部變量。 當(dāng)啟用 CLS 時,創(chuàng)建新事務(wù)時,Sequelize 將在命名空間上設(shè)置 transaction 屬性。 由于回調(diào)鏈中設(shè)置的變量對該鏈?zhǔn)撬接械?,因此可以同時存在多個并發(fā)事務(wù):
sequelize.transaction(function (t1) {
namespace.get('transaction') === t1; // true
});
sequelize.transaction(function (t2) {
namespace.get('transaction') === t2; // true
});
在大多數(shù)情況下,你不需要直接訪問 namespace.get('transaction'),因為所有查詢都將自動在命名空間中查找事務(wù):
sequelize.transaction(function (t1) {
// 啟用 CLS 后,將在事務(wù)中創(chuàng)建用戶
return User.create({ name: 'Alice' });
});
在使用 Sequelize.useCLS() 后,從 sequelize 返回的所有 promise 將被修補以維護 CLS 上下文。 CLS 是一個復(fù)雜的課題 - cls-bluebird的文檔中有更多細(xì)節(jié),用于使 bluebird promise 的補丁與CLS一起工作。
注意: 當(dāng)使用 cls-hooked 軟件包的時候,CLS 僅支持 async/await. 即使, cls-hooked 依靠 試驗性的 API async_hooks
并行/部分事務(wù)
你可以在一系列查詢中執(zhí)行并發(fā)事務(wù),或者將某些事務(wù)從任何事務(wù)中排除。 使用 {transaction: } 選項來控制查詢所屬的事務(wù):
警告: SQLite 不能同時支持多個事務(wù)。
不啟用CLS
sequelize.transaction(function (t1) {
return sequelize.transaction(function (t2) {
// 啟用CLS,這里的查詢將默認(rèn)使用 t2
// 通過 `transaction` 選項來定義/更改它們所屬的事務(wù)。
return Promise.all([
User.create({ name: 'Bob' }, { transaction: null }),
User.create({ name: 'Mallory' }, { transaction: t1 }),
User.create({ name: 'John' }) // 這將默認(rèn)為 t2
]);
});
});
隔離等級
啟動事務(wù)時可能使用的隔離等級:
Sequelize.Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED // "READ UNCOMMITTED"
Sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED // "READ COMMITTED"
Sequelize.Transaction.ISOLATION_LEVELS.REPEATABLE_READ // "REPEATABLE READ"
Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE // "SERIALIZABLE"
默認(rèn)情況下,sequelize 使用數(shù)據(jù)庫的隔離級別。 如果要使用不同的隔離級別,請傳入所需級別作為第一個參數(shù):
return sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
}, function (t) {
// 你的事務(wù)
});
注意: 在MSSQL的情況下,SET ISOLATION LEVEL 查詢不被記錄, 指定的 isolationLevel 直接傳遞到 tedious
非托管事務(wù)(then-callback)
非托管事務(wù)強制您手動回滾或提交交易。 如果不這樣做,事務(wù)將掛起,直到超時。 要啟動非托管事務(wù),請調(diào)用 sequelize.transaction() 而不用 callback(你仍然可以傳遞一個選項對象),并在返回的 promise 上調(diào)用 then。 請注意,commit() 和 rollback() 返回一個 promise。
return sequelize.transaction().then(function (t) {
return User.create({
firstName: 'Bart',
lastName: 'Simpson'
}, {transaction: t}).then(function (user) {
return user.addSibling({
firstName: 'Lisa',
lastName: 'Simpson'
}, {transaction: t});
}).then(function () {
return t.commit();
}).catch(function (err) {
return t.rollback();
});
});
參數(shù)
可以使用options對象作為第一個參數(shù)來調(diào)用transaction方法,這允許配置事務(wù)。
return sequelize.transaction({ /* options */ });
以下選項(使用默認(rèn)值)可用:
{
autocommit: true,
isolationLevel: 'REPEATABLE_READ',
deferrable: 'NOT DEFERRABLE' // postgres 的默認(rèn)設(shè)置
}
在為 Sequelize 實例或每個局部事務(wù)初始化時,isolationLevel可以全局設(shè)置:
// 全局
new Sequelize('db', 'user', 'pw', {
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
// 局部
sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
deferrable 選項在事務(wù)開始后觸發(fā)一個額外的查詢,可選地將約束檢查設(shè)置為延遲或立即。 請注意,這僅在PostgreSQL中受支持。
sequelize.transaction({
// 推遲所有約束:
deferrable: Sequelize.Deferrable.SET_DEFERRED,
// 推遲具體約束:
deferrable: Sequelize.Deferrable.SET_DEFERRED(['some_constraint']),
// 不推遲約束:
deferrable: Sequelize.Deferrable.SET_IMMEDIATE
})
使用其他 Sequelize 方法
transaction 選項與其他大多數(shù)選項一起使用,通常是方法的第一個參數(shù)。
對于取值的方法,如 .create, .update(), .updateAttributes() 等。應(yīng)該傳遞給第二個參數(shù)的選項。
如果不確定,請參閱API文檔中的用于確定簽名的方法。
后提交 hook
transaction 對象允許跟蹤是否提交以及何時提交。
可以將 afterCommit hook 添加到托管和非托管事務(wù)對象:
sequelize.transaction(t => {
t.afterCommit((transaction) => {
// 你的邏輯片段
});
});
sequelize.transaction().then(t => {
t.afterCommit((transaction) => {
// 你的邏輯片段
});
return t.commit();
})
傳遞給 afterCommit 的函數(shù)可以有選擇地返回一個承諾,在創(chuàng)建事務(wù)的承諾鏈解析之前這個承諾會被解析。
afterCommit 如果事務(wù)回滾,hook 不會 被提升。
afterCommit hook 不像標(biāo)準(zhǔn) hook, 不會 修改事務(wù)的返回值。
您可以將 afterCommit hook 與模型 hook 結(jié)合使用,以了解實例何時在事務(wù)外保存并可用
model.afterSave((instance, options) => {
if (options.transaction) {
// 在事務(wù)內(nèi)保存完成,等到事務(wù)被提交以通知監(jiān)聽器實例已被保存
options.transaction.afterCommit(() => /* Notify */)
return;
}
// 在事務(wù)之外保存完成,對于調(diào)用者來說可以安全地獲取更新后的模型通知