Use Continuation Local Storage. This assigns the global-level Sequelize package to a "namespace", so that all instances created from it reference the namespace when performing transactions.
You initialise Sequelize as follows (assuming ES6 import syntax):
// Grab packages we need
import Sequelize from 'sequelize';
import Cls from 'continuation-local-storage';
// Assign namespace to database
Sequelize.cls = Cls.createNamespace('db');
This then allows you to perform transactions without explicitly passing t
around. It also rolls back on uncaught exceptions (or technically, unresolved promises), and commits on resolved promises:
The following is a sample function I'm using in production code that demonstrates the concept in action.
It...
- Starts a transaction (
BEGIN;
in PostgreSQL)
- Creates a new account (
INSERT INTO "accounts"...
)
- Creates an entry that joins an account to an account type (
INSERT INTO "account_type_accounts"...
)
- Creates an entry that links the user to an account (
INSERT INTO "users_accounts"...
)
- Only performs the inserts if ALL of above succeeded (
COMMIT;
). If not, it rolls back (ROLLBACK;
)
Here's the code:
createAccount (user, accountType, query = {}) {
// Start transaction
return this.db.connection.transaction(() => {
// Create the new account
return this.db.models.Account.create(query).then(account => {
// Associate with user & account type
return P.all([user.addAccount(account), accountType.addAccount(account)]).then(()=> {
// Return account back up the promise chain
return account;
});
});
});
}
Note the lack of t
variables or explicit rollback/commit.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…