Transactions - Transactions
Examples of applications for this series of articles have been published. GitHub: sequelize-docs-Zh-CN. Fork can help improve or Star can focus on updates. Welcome Star.
Sequelize supports two ways of using transactions:
- A transaction that will automatically commit or roll back based on the result of the promise chain (if enabled) is passed back to all calls with callbacks
- Another leave committing rolls back and passes the transaction to the user.
The main difference is that managed transactions use a callback and expect promise to return a promise result for unmanaged transactions.
auto-callback
Managed transactions automatically handle commit or rollback transactions. You can start managed transactions by passing callbacks to sequelize.transaction.
Notice whether the callback passed back to transaction is a promise chain and does not explicitly call t.commit () or t.rollback().? If all promises in the return chain have been successfully resolved, the transaction is committed. If one or more promises are rejected, the transaction rolls back.
return sequelize.transaction(function (t) {
// Link all your queries here. Make sure you return to them.
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(function (user) {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {transaction: t});
});
}).then(function (result) {
// Transactions have been committed
// Result is the result of the promise chain returning to the transaction callback
}).catch(function (err) {
// The transaction has been rolled back
// Er is an err or that rejects a promise chain to return to a transaction callback
});
Throw an error to rollback
When using managed transactions, you should never submit or roll back transactions manually. If all queries succeed, but you still want to roll back transactions (for example, because validation fails), you should throw an error to disconnect and reject links:
return sequelize.transaction(function (t) {
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(function (user) {
// The query succeeded, but we still want to roll back!
throw new Error();
});
});
Automatically pass transactions to all queries
In the above example, the transaction is still passed manually by passing {transaction:t} as the second parameter. To automatically pass transactions to all queries, you must install continuation local storage (CLS) module and instantiate a namespace in your own code:
const cls = require('continuation-local-storage'),
namespace = cls.createNamespace('my-very-own-namespace');
To enable CLS, you must tell Sequelize the namespace to use by using the static method of the sequelize constructor:
const Sequelize = require('sequelize');
Sequelize.useCLS(namespace);
new Sequelize(....);
Note that the useCLS() method is on the constructor, not on the sequelize instance. This means that all instances will share the same namespace, and CLS is all or nothing - you can't just enable it in some instances.
CLS works like a local thread store for callbacks. This means in practice that different callback chains can access local variables by using CLS namespaces. When CLS is enabled, when a new transaction is created, Sequelize sets the transaction property on the namespace. Since the variables set in the callback chain are private to the chain, multiple concurrent transactions can exist simultaneously:
sequelize.transaction(function (t1) {
namespace.get('transaction') === t1; // true
});
sequelize.transaction(function (t2) {
namespace.get('transaction') === t2; // true
});
In most cases, you don't need direct access to namespace.get('transaction'), because all queries will automatically find transactions in the namespace:
sequelize.transaction(function (t1) {
// When CLS is enabled, users are created in transactions
return User.create({ name: 'Alice' });
});
After using Sequelize.useCLS(), all promise s returned from sequelize will be patched to maintain the CLS context. CLS is a complex subject. cls-bluebird There are more details in the document to make the bluebird promise patch work with CLS.
Parallel/Partial Transactions
You can execute concurrent transactions in a series of queries, or exclude certain transactions from any transaction. Use the {transaction:} option to control the transaction to which the query belongs:
No CLS enabled
sequelize.transaction(function (t1) {
return sequelize.transaction(function (t2) {
// With CLS enabled, the query here will default to t2
// Define/change the transactions to which they belong through the `transaction'option.
return Promise.all([
User.create({ name: 'Bob' }, { transaction: null }),
User.create({ name: 'Mallory' }, { transaction: t1 }),
User.create({ name: 'John' }) // This will default to t2
]);
});
});
Isolation level
The isolation level that may be used when starting a transaction:
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"
By default, sequelize uses the isolation level of the database. If you want to use different isolation levels, pass in the required level as the first parameter:
return sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
}, function (t) {
// Your affairs
});
Note: In the case of MSSQL, the SET ISOLATION LEVEL query is not recorded, and the specified isolation level is passed directly to tedious.
then-callback
Unmanaged transactions force you to roll back or submit transactions manually. If you don't, the transaction hangs until the timeout occurs. To start an unmanaged transaction, call sequelize.transaction() instead of callback (you can still pass an option object) and call then on the promise returned. Note that commit() and rollback() return one promise.
return sequelize.transaction().then(function (t) {
return User.create({
firstName: 'Homer',
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();
});
});
parameter
You can use the options object as the first parameter to invoke the transaction method, which allows you to configure transactions.
return sequelize.transaction({ /* options */ });
The following options (using default values) are available:
{
autocommit: true,
isolationLevel: 'REPEATABLE_READ',
deferrable: 'NOT DEFERRABLE' // Default settings for postgres
}
When initializing a Sequelize instance or each local transaction, isolationLevel can be set globally:
// Overall situation
new Sequelize('db', 'user', 'pw', {
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
// local
sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
});
The deferrable option triggers an additional query after the transaction starts, optionally setting constraint checks to delay or immediately. Note that this is supported only in PostgreSQL.
sequelize.transaction({
// Delay all constraints:
deferrable: Sequelize.Deferrable.SET_DEFERRED,
// Delay specific constraints:
deferrable: Sequelize.Deferrable.SET_DEFERRED(['some_constraint']),
// Without deferring constraints:
deferrable: Sequelize.Deferrable.SET_IMMEDIATE
})
Use other Sequelize methods
The transaction option is used with most other options, usually the first parameter of a method.
For methods of value selection, such as. create,. update(),. updateAttributes(), etc. Options that should be passed to the second parameter.
If you are not sure, please refer to the method used to determine signatures in the API documentation.