You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeORM事务中未提交企业行重复创建问题解决方案咨询

问题

我有个companies表,taxId列加了唯一约束(UQ)。代码里写了companiesRepository类,包含三个方法:

  • findCompany:通过taxId查询企业
  • createCompany:执行INSERT操作创建企业
  • findOrCreate:传入taxId,先调用findCompany查询,未找到则调用createCompany创建

现在用TypeORM的queryRunner管理全局事务,处理接口接收的企业+发票树形结构数据,确保流程出错时能回滚。但同一请求的payload里可能包含多张同一家未注册企业的发票:第一次调用findOrCreate会创建该企业,但由于事务未提交,后续调用findOrCreate查不到这条未提交的记录,会尝试重复创建,触发唯一约束违例异常。

我目前有两个思路:取消企业创建的事务(可行但不合理);用单独的queryManager创建企业并立即提交(能让后续查询到)。但想知道:能不能在当前接口的全局queryManager事务内,查询到未提交的行?

解决方案

当然可以,问题出在操作的连接绑定或者事务隔离级别上,下面是具体解决办法:

1. 所有操作绑定同一个事务实例

TypeORM中,事务内的所有读写必须使用同一个QueryRunner实例(或者它生成的EntityManager),否则后续查询会走独立的数据库连接,看不到当前事务未提交的变更。

比如你的companiesRepository方法如果用的是全局EntityManager而非事务绑定的实例,就会出现查不到未提交数据的情况。正确做法:

  • 事务启动后,用queryRunner.manager获取绑定当前事务的EntityManager
  • 所有findCompany、createCompany调用都使用这个事务专属的实例执行

示例代码:

// 开启事务
const queryRunner = connection.createQueryRunner();
await queryRunner.startTransaction();

try {
  // 获取事务绑定的EntityManager
  const txEntityManager = queryRunner.manager;
  
  // 处理每张发票时,传入这个EntityManager到findOrCreate
  for (const invoice of invoices) {
    await companiesRepository.findOrCreate(invoice.taxId, txEntityManager);
  }

  await queryRunner.commitTransaction();
} catch (err) {
  await queryRunner.rollbackTransaction();
} finally {
  await queryRunner.release();
}

// 修改findOrCreate方法,接收事务EntityManager
async findOrCreate(taxId: string, entityManager: EntityManager) {
  // 用事务实例查询
  const company = await entityManager.findOne(Company, { where: { taxId } });
  if (company) return company;
  
  // 用事务实例创建并保存
  const newCompany = entityManager.create(Company, { taxId });
  return entityManager.save(newCompany);
}

2. 调整事务隔离级别

如果数据库默认隔离级别是REPEATABLE READ(比如MySQL默认),事务内后续查询可能会读取事务启动时的快照,看不到新创建的行。可以手动设置为READ COMMITTED,此时同一事务内就能看到自己未提交的变更:

await queryRunner.startTransaction("READ COMMITTED");

3. 本地缓存优化(可选)

还可以在请求上下文里维护一个内存缓存,保存taxId -> Company的映射:每次调用findOrCreate先查缓存,命中直接返回;未命中再走数据库查询+创建,创建后同步到缓存。这样既能避免重复创建,又不破坏事务的原子性。

总结

核心问题是事务内操作未绑定同一个连接/EntityManager,只要把所有读写都挂到当前事务的QueryRunner上,就能在事务内查询到未提交的行,完全不用拆分事务或者单独提交企业创建操作。

内容的提问来源于stack exchange,提问作者sampaiorafael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 10:55:23