跳到主要内容

事务(Transactions)

创建和使用事务

使用 DataSourceEntityManager 来创建事务。 示例:

await myDataSource.transaction(async (transactionalEntityManager) => {
// 使用 transactionalEntityManager 执行查询
})

await myDataSource.manager.transaction(async (transactionalEntityManager) => {
// 使用 transactionalEntityManager 执行查询
})

你希望在事务中运行的所有内容都必须在回调中执行:

await myDataSource.manager.transaction(async (transactionalEntityManager) => {
await transactionalEntityManager.save(users)
await transactionalEntityManager.save(photos)
// ...
})

在事务中工作时最重要的限制是要始终使用提供的实体管理器实例——在这个示例中就是 transactionalEntityManager。不要使用全局实体管理器。 所有操作必须使用提供的事务性实体管理器来执行。

指定隔离级别

通过将其作为第一个参数传入,可以为事务指定隔离级别:

await myDataSource.manager.transaction(
"SERIALIZABLE",
(transactionalEntityManager) => {},
)

支持的隔离级别

隔离级别的实现并不对所有数据库都保持一致。每个驱动会声明它支持哪些级别,如果你请求了不支持的级别,TypeORM 将抛出错误。

CockroachDB

  • READ COMMITTED — 需要集群设置 sql.txn.read_committed_isolation.enabled,该设置在较新版本中默认启用
  • REPEATABLE READ — 需要集群设置 sql.txn.repeatable_read_isolation.enabled,该设置默认禁用(在 v24.3.0 中引入)
  • SERIALIZABLE(默认)

CockroachDB 会将较弱的 SQL 隔离级别请求映射为更强的级别。回退行为取决于对应的集群设置。例如,当你通过 SQL 请求 READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READ(例如 BEGIN TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)时,CockroachDB 会接受语法,但可能会改以更严格的级别运行事务:

  • READ COMMITTED → 运行方式:
    • 如果 sql.txn.read_committed_isolation.enabled 为 false 且 sql.txn.repeatable_read_isolation.enabled 为 false,则为 SERIALIZABLE
    • 如果 sql.txn.repeatable_read_isolation.enabled 为 true 且 sql.txn.read_committed_isolation.enabled 为 false,则为 REPEATABLE READ
  • READ UNCOMMITTED → 运行方式:
    • 如果 sql.txn.read_committed_isolation.enabled 为 false 且 sql.txn.repeatable_read_isolation.enabled 为 false,则为 SERIALIZABLE
    • 如果 sql.txn.read_committed_isolation.enabled 为 true,则为 READ COMMITTED
    • 如果 sql.txn.repeatable_read_isolation.enabled 为 true 且 sql.txn.read_committed_isolation.enabled 为 false,则为 REPEATABLE READ
  • REPEATABLE READ → 如果 sql.txn.repeatable_read_isolation.enabled 为 false,则以 SERIALIZABLE 运行

默认隔离级别

你可以通过在 DataSource 选项中设置 isolationLevel 来为所有事务配置默认隔离级别:

const dataSource = new DataSource({
type: "postgres",
isolationLevel: "SERIALIZABLE",
// ...
})

设置后,所有在未显式指定隔离级别的情况下启动的事务都将使用这个默认值。传给 transaction()startTransaction() 的显式隔离级别会覆盖默认值。

使用 QueryRunner 来创建并控制单个数据库连接的状态

QueryRunner 提供单个数据库连接。 通过 query runner 来组织事务。 单个事务只能在同一个 query runner 上建立。 你可以手动创建一个 query runner 实例,并使用它来手动控制事务状态。 示例:

// 创建一个新的 query runner
const queryRunner = dataSource.createQueryRunner()

// 使用我们新的 query runner 建立真实的数据库连接
await queryRunner.connect()

// 现在我们可以在 query runner 上执行任何查询,例如:
await queryRunner.query("SELECT * FROM users")

// 我们也可以访问与 query runner 创建的连接协同工作的实体管理器:
const users = await queryRunner.manager.find(User)

// 现在打开一个新的事务:
await queryRunner.startTransaction()

try {
// 在这个事务上执行一些操作:
await queryRunner.manager.save(user1)
await queryRunner.manager.save(user2)
await queryRunner.manager.save(photos)

// 现在提交事务:
await queryRunner.commitTransaction()
} catch (err) {
// 因为发生了错误,所以回滚我们做出的更改
await queryRunner.rollbackTransaction()
} finally {
// 你需要释放手动创建的 query runner:
await queryRunner.release()
}

QueryRunner 中有 3 种方法可以控制事务:

  • startTransaction - 在 query runner 实例中启动一个新的事务。
  • commitTransaction - 提交使用 query runner 实例所做的所有更改。
  • rollbackTransaction - 回滚使用 query runner 实例所做的所有更改。

了解更多关于 Query Runner 的内容。