Stark Jeon
Stark Jeon

Reputation: 1145

TypeORM transaction with query builder

I have some question with TypeORM transaction. So my code is like below:

  public createOrderTransaction = async (
    data: CreateOrderInputDTO,
  ): Promise<Order> => {
    const queryRunner = this.connection.createQueryRunner();
    await queryRunner.startTransaction();
    try {
      const order = await this.createOrder(data);
      await this.createOrderRecord(order.id, data);
      await queryRunner.commitTransaction();
      return order;
    } catch (error) {
      this.logger.error(
        "Transaction Error: 'createOrderTransaction' is failed: ",
        error,
      );
      if (queryRunner.isTransactionActive) {
        await queryRunner.rollbackTransaction();
      }
    }
  };

And one function like createOrder use query builder:

private createOrder = async (data: CreateOrderInputDTO): Promise<Order> => {
    const { identifiers } = await this.orderRepository
      .createQueryBuilder('order')
      .insert()
      .into(Order)
      .values([data])
      .execute();

    await this.saveOrderDetail(identifiers[0].id, data.orderDetails);

    await this.orderRepository
      .createQueryBuilder('order')
      .relation(Order, 'orderDetails')
      .of(identifiers[0].id)
      .add(data.orderDetails);

    return this.getOrder(identifiers[0].id);
  };

But when I try it, and if it fire some error middle of logic, rollback doesn't work. The way I see it, It seem that query builder doesn't support transaction. So Is there any way to use quert builder with transaction?

Upvotes: 4

Views: 10689

Answers (2)

Jacky
Jacky

Reputation: 3239

I don't know if this helps help anyone facing this question, but instead of doing in above answer, we can use dataSource, as suggested in this official TypeOrm transaction document, and instead of adding Entity in queryBuilder, add it to .from()

await myDataSource.manager.transaction(async (transactionalEntityManager) => {
// We can use 'transactionalEntityManager' to create queryBuilder here:
await transactionalEntityManager.createQueryBuilder()
  .from(Order, 'order')
  .insert()
  .into(OrderRecord)
  .values([
    {
      ...data,
      orderId,
      createdAt: new Date(),
    },
  ])
  .execute();

// And of course similar with relation table:
await transactionalEntityManager
  .createQueryBuilder()
  .from(Order, 'order')
  .relation(OrderRecord, 'orderDetails')
  .of(orderId)
  .add(data.orderDetails);
})

Upvotes: 0

Stark Jeon
Stark Jeon

Reputation: 1145

Self Answer... After I am looking into TypeORM index.d.ts, I take it that there are some parameter in createQueryBuilder named queryRunner so. If you input there your own query runner for transaction you can use queryBuilder with queryRunner

As like:

  public createOrderTransaction = async (
    data: CreateOrderInputDTO,
  ): Promise<Order> => {
    const queryRunner = this.connection.createQueryRunner();

    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const order = await this.createOrder(data, queryRunner);
      await this.createOrderRecord(order.id, data, queryRunner);
      await queryRunner.commitTransaction();
      return order;
    } catch (error) {
      this.logger.error(
        "Transaction Error: 'createOrderTransaction' is failed: ",
        error,
      );
      if (queryRunner.isTransactionActive) {
        await queryRunner.rollbackTransaction();
      }
    }
  };

createOrder as like:

 private createOrderRecord = async (
    orderId: string,
    data: CreateOrderInputDTO,
    queryRunner?: QueryRunner,
  ): Promise<void> => {
    await this.orderRecordRepository
      .createQueryBuilder('order_record', queryRunner)
      .insert()
      .into(OrderRecord)
      .values([
        {
          ...data,
          orderId,
          createdAt: new Date(),
        },
      ])
      .execute();

    await this.orderRecordRepository
      .createQueryBuilder('order_record', queryRunner)
      .relation(OrderRecord, 'orderDetails')
      .of(orderId)
      .add(data.orderDetails);
  };

Upvotes: 3

Related Questions