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

如何在TypeORM批量插入时基于shortLink取消单条插入?

解决TypeORM批量插入时基于非主键字段校验并跳过重复项的问题

TypeORM的save()方法默认通过主键判断执行插入或更新,无法直接基于shortLink这类非主键字段做存在性校验;而在beforeInsert事件中抛出错误会终止整个批量插入操作,无法仅跳过重复项。以下是两种可行的实现方式:

方案一:提前预处理过滤重复项

在批量插入前,先为每个链接生成shortLink并检查是否已存在,仅对不存在的项执行插入操作。这种方式逻辑清晰,能避免后续事务处理的额外开销。

步骤1:为shortLink添加数据库唯一约束(兜底防并发)

首先在实体类中给shortLink字段添加唯一约束,防止并发场景下出现重复插入:

@Entity()
export class QuickLink {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  actualLink: string;

  @Column()
  @Unique() // 添加唯一约束
  shortLink: string;
}

步骤2:修改Service层的批量处理方法

async shortLinks(links: Array<string>): Promise<Array<QuickLinkDto>> {
  // 并行处理每个链接:生成shortLink并检查是否存在
  const processedItems = await Promise.all(links.map(async (link) => {
    const shortLink = await getShortLink(link);
    const isExists = await this.quickLinkRepository.exists({ where: { shortLink } });
    
    return isExists ? null : { actualLink: link, shortLink };
  }));

  // 过滤掉已存在的项,只保留需要插入的
  const needInsertItems = processedItems.filter(item => item !== null);

  // 批量插入有效项
  const insertedItems = await this.quickLinkRepository.save(needInsertItems);

  // 若需返回所有请求链接的对应结果(包括已存在的),可合并查询结果
  const allResults = await Promise.all(links.map(async (link) => {
    const shortLink = await getShortLink(link);
    const dbItem = await this.quickLinkRepository.findOne({ where: { shortLink } });
    return { actualLink: dbItem.actualLink, shortLink: dbItem.shortLink };
  }));

  return allResults;
}

方案二:事务内逐个处理并捕获重复错误

通过创建事务,逐个处理每个链接的插入操作,捕获数据库唯一约束的错误并跳过重复项。这种方式依赖数据库约束,能有效避免并发竞态问题。

async shortLinks(links: Array<string>): Promise<Array<QuickLinkDto>> {
  const results: QuickLinkDto[] = [];
  const queryRunner = this.quickLinkRepository.manager.connection.createQueryRunner();

  await queryRunner.connect();
  await queryRunner.startTransaction();

  try {
    for (const link of links) {
      const shortLink = await getShortLink(link);
      const quickLink = queryRunner.manager.create(QuickLink, { actualLink: link, shortLink });

      try {
        // 尝试插入当前项
        const savedItem = await queryRunner.manager.save(quickLink);
        results.push({ actualLink: savedItem.actualLink, shortLink: savedItem.shortLink });
      } catch (err) {
        // 捕获唯一约束违反错误(PostgreSQL错误码为23505,MySQL为1062,需根据数据库调整)
        if (err.code === '23505') {
          // 查询已存在的项并加入结果
          const existingItem = await queryRunner.manager.findOne(QuickLink, { where: { shortLink } });
          results.push({ actualLink: existingItem.actualLink, shortLink: existingItem.shortLink });
        } else {
          // 非重复错误,回滚事务并抛出
          throw err;
        }
      }
    }

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

  return results;
}

为什么beforeInsert无法实现需求?

TypeORM的批量插入操作中,beforeInsert是全局钩子,一旦在某个实体的钩子中抛出错误,整个批量插入流程会被终止,无法单独跳过当前重复项。因此需要通过上述两种方式绕开这个限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 13:40:33