# Transactions
- Creating and using transactions
- Using
QueryRunner
to create and control state of single database connection
# Creating and using transactions
Transactions are created using DataSource
or EntityManager
.
Examples:
await myDataSource.transaction(async (transactionalEntityManager) => {
// execute queries using transactionalEntityManager
})
2
3
or
await myDataSource.manager.transaction(async (transactionalEntityManager) => {
// execute queries using transactionalEntityManager
})
2
3
Everything you want to run in a transaction must be executed in a callback:
await myDataSource.manager.transaction(async (transactionalEntityManager) => {
await transactionalEntityManager.save(users)
await transactionalEntityManager.save(photos)
// ...
})
2
3
4
5
The most important restriction when working in a transaction is to ALWAYS use the provided instance of entity manager -
transactionalEntityManager
in this example. DO NOT USE GLOBAL ENTITY MANAGER.
All operations MUST be executed using the provided transactional entity manager.
# Specifying Isolation Levels
Specifying the isolation level for the transaction can be done by supplying it as the first parameter:
await myDataSource.manager.transaction(
"SERIALIZABLE",
(transactionalEntityManager) => {},
)
2
3
4
Isolation level implementations are not agnostic across all databases.
The following database drivers support the standard isolation levels (READ UNCOMMITTED
, READ COMMITTED
, REPEATABLE READ
, SERIALIZABLE
):
- MySQL
- Postgres
- SQL Server
SQLite defaults transactions to SERIALIZABLE
, but if shared cache mode is enabled, a transaction can use the READ UNCOMMITTED
isolation level.
Oracle only supports the READ COMMITTED
and SERIALIZABLE
isolation levels.
# Using QueryRunner
to create and control state of single database connection
QueryRunner
provides a single database connection.
Transactions are organized using query runners.
Single transactions can only be established on a single query runner.
You can manually create a query runner instance and use it to manually control transaction state.
Example:
// create a new query runner
const queryRunner = dataSource.createQueryRunner()
// establish real database connection using our new query runner
await queryRunner.connect()
// now we can execute any queries on a query runner, for example:
await queryRunner.query("SELECT * FROM users")
// we can also access entity manager that works with connection created by a query runner:
const users = await queryRunner.manager.find(User)
// lets now open a new transaction:
await queryRunner.startTransaction()
try {
// execute some operations on this transaction:
await queryRunner.manager.save(user1)
await queryRunner.manager.save(user2)
await queryRunner.manager.save(photos)
// commit transaction now:
await queryRunner.commitTransaction()
} catch (err) {
// since we have errors let's rollback changes we made
await queryRunner.rollbackTransaction()
} finally {
// you need to release query runner which is manually created:
await queryRunner.release()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
There are 3 methods to control transactions in QueryRunner
:
startTransaction
- starts a new transaction inside the query runner instance.commitTransaction
- commits all changes made using the query runner instance.rollbackTransaction
- rolls all changes made using the query runner instance back.
Learn more about Query Runner.
← Migrations Indices →