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);
  };
Answer from Stark Jeon on Stack Overflow
Top answer
1 of 2
3

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);
  };
2 of 2
0

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);
})
🌐
TutorialsPoint
tutorialspoint.com › typeorm › typeorm_transactions.htm
TypeORM - Transactions
Lets perform single transaction using QueryRunner. import {getConnection} from "typeorm"; // get a connection and create a new query runner const connection = getConnection(); const queryRunner = connection.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 students");
Discussions

TypeOrm transaction doesn't work [Help]
ETA: Ignore below. I misread the second paragraph as the desired behavior. Start an outer transaction. Insert the Food item. Start a nested transaction, and insert FoodStore. Start another nested transaction (as a sibling to #3), and insert the tags. More on reddit.com
🌐 r/node
12
2
September 17, 2023
sql - How to execute multiple QueryBuilders in a transaction using TypeORM - Stack Overflow
0 How to execute multiple raw SQL commands in a single transaction? 3 TypeORM: How to run SELECT... FOR UPDATE inside queryRunner? 0 TypeORM do not select give any data from queryBuilder More on stackoverflow.com
🌐 stackoverflow.com
TypeORM transaction saving entity to database despite the next transaction step failing - Stack Overflow
I want to make a transaction to save a new table row and the many-to-many helper table entry for that row. I'm saving both using the same transaction manager but, when the second part fails, the fi... More on stackoverflow.com
🌐 stackoverflow.com
Queries being executed outside transaction
Current version: 0.1.0-alpha.33 The problem Im having is that certain queries that are being run with an EntityManager that is transacting arent being run in the transaction. This is how Im creatin... More on github.com
🌐 github.com
3
August 29, 2017
🌐
GitHub
github.com › typeorm › typeorm › issues › 1890
sqljs - Transaction rollback not work with querybuilder · Issue #1890 · typeorm/typeorm
April 5, 2018 - const connection = getConnection(); const queryRunner = connection.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const queryBuilder = connection.createQueryBuilder(queryRunner); const res = await queryBuilder .insert() .into(Category) .values([{ name: "Timber" }]) .execute(); throw ('forced error'); //await queryRunner.commitTransaction(); } catch (error) { console.log("isTransactionActive?", queryRunner.isTransactionActive); if (queryRunner.isTransactionActive) { await queryRunner.rollbackTransaction(); } } ... executing query: BEGIN TRANSACTION executing query: INSERT INTO "category"("name") VALUES ($1) -- PARAMETERS: ["Timber"] isTransactionActive?
Author   typeorm
🌐
TypeORM
typeorm.io
TypeORM - Code with Confidence. Query with Power. | TypeORM
TypeORM provides a beautiful, simple API for interacting with your database that takes full advantage of TypeScript's type system.
🌐
TypeORM
typeorm.io › datasource api
DataSource API | TypeORM
transaction - Provides a single transaction where multiple database requests will be executed in a single database transaction. Learn more about Transactions. ... Learn more about using the SQL Tag syntax. createQueryBuilder - Creates a query ...
🌐
TypeORM
typeorm.biunav.com › en › transactions.html
Transactions | TypeORM Docs
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.
🌐
Reddit
reddit.com › r/node › typeorm transaction doesn't work [help]
r/node on Reddit: TypeOrm transaction doesn't work [Help]
September 17, 2023 -

I'm trying to run a transaction that'll wrap 3 queries that must be executed or rollback if one of the fails.

If the 2nd query fails, the transaction doesn't roolback and the 1st query persists the data anyway, the error message i'm getting is : Query failed: SelectSQL: queryAll: cannot rollback - no transaction is active i want to use the querybuilder for this, is it possible ? i also tried to use the repositories but i'm getting the same error message

Here's the code i'm trying to execute :

await appDatasource.transaction(async (tem) => {   
const qb = await tem.createQueryBuilder();   

// Insert into food table   const foodRes = await qb.insert().into(Food).values(food).updateEntity(false).execute();   

const foodId: number = foodRes.raw;    
// Insert into food_x_store table   
foodXStore.forEach(fxS => fxS.foodId = foodId);    
await qb.insert().into(FoodXStore).values(foodXStore).updateEntity(false).execute();    

// Insert into food_x_tag table   
const foodXTagJoins: FoodXTag[] = foodDto.tagIds!.map((tagId) => ({ tagId, foodId }));   
await qb.insert().into(FoodXTag).values(foodXTagJoins).updateEntity(false).execute(); })

I also tried this version of the code that starts a transaction beforehand :

    const qr = appDatasource.createQueryRunner();
    await qr.startTransaction();

    const res = await appDatasource.transaction(async (tem) => {
      const qb = await tem.createQueryBuilder();
      // Insert into food table
      const foodRes = await tem.createQueryBuilder().insert().into(Food).values(food).updateEntity(false).execute();
      const foodId: number = foodRes.raw;
      console.log("INSERT FOOD RES : ", foodRes);

      // Insert into food_x_store table
      foodXStore.forEach(fxS => fxS.foodId = foodId);

      await tem.createQueryBuilder().insert().into(FoodXStore).values(foodXStore).updateEntity(false).execute();

      // Insert into food_x_tag table
      const foodXTagJoins: FoodXTag[] = foodDto.tagIds!.map((tagId) => ({ tagId, foodId }));
      await tem.createQueryBuilder().insert().into(FoodXTag).values(foodXTagJoins).updateEntity(false).execute();
    })

But this doesn't work either, when it tries to execute the 2nd query (inserting joins) it fails because the 1st one isn't commited yet (i can see the created ID in the logs but it's not persisted in the database)

EDIT : Github issue with some more info

https://github.com/typeorm/typeorm/issues/10364

🌐
TypeORM
typeorm.io › entitymanager api
EntityManager API | TypeORM
transaction - Provides a transaction where multiple database requests will be executed in a single database transaction. Learn more Transactions. ... Learn more about using the SQL Tag syntax. createQueryBuilder - Creates a query builder use ...
Find elsewhere
🌐
HackerNoon
hackernoon.com › the-most-convenient-ways-of-writing-transactions-within-the-nestjs-typeorm-stack-3q3q33jd
The Most Convenient Ways of Writing Transactions Within the Nest.js + TypeORM Stack | HackerNoon
February 28, 2021 - // ... SELECT "User"."id" AS "User_id", "User"."name" AS "User_name", "User"."defaultPurseId" AS "User_defaultPurseId" FROM "user" "User" WHERE "User"."id" IN ($1) START TRANSACTION UPDATE "purse" SET "balance" = $2 WHERE "id" IN ($1) COMMIT SELECT "Purse"."id" AS "Purse_id", "Purse"."balance" AS "Purse_balance", "Purse"."userId" AS "Purse_userId" FROM "purse" "Purse" WHERE "Purse"."id" IN ($1) START TRANSACTION UPDATE "purse" SET "balance" = $2 WHERE "id" IN ($1) COMMIT · By the way - I did not write this request by hand, but pulled it out of the ORM logs, but it reflects the essence. Everything is pretty simple and straightforward. To build the queries, TypeORM was used, which we will return to a little later.
🌐
TypeORM
typeorm.io › repository apis
Repository APIs | TypeORM
... target - The target entity class managed by this repository. Used only in transactional instances of EntityManager. ... createQueryBuilder - Creates a query builder use to build SQL queries. Learn more about QueryBuilder...
🌐
TypeORM
typeorm.io › getting started
Getting Started | TypeORM
Transactions. Migrations with automatic generation. Connection pooling. Replication. Using multiple database instances. Working with multiple database types. Cross-database and cross-schema queries. Elegant-syntax, flexible and powerful QueryBuilder.
🌐
TypeORM
typeorm.io › select using query builder
Select using Query Builder | TypeORM
... QueryBuilder is one of the most powerful features of TypeORM - it allows you to build SQL queries using elegant and convenient syntax, execute them and get automatically transformed entities.
🌐
Deno
deno.land › x › typeorm@v0.2.23-rc8 › docs › transactions.md
/docs/transactions.md | typeorm@v0.2.23-rc8 | Deno
You can manually create a query runner instance and use it to manually control transaction state. Example: import {getConnection} from "typeorm"; // get a connection and create a new query runner const connection = getConnection(); const queryRunner ...
🌐
TypeORM
typeorm.io › transactions
Transactions | TypeORM
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 ...
🌐
GitHub
github.com › typeorm › typeorm › issues › 804
Queries being executed outside transaction · Issue #804 · typeorm/typeorm
August 29, 2017 - Now, if I run pretty much any query like entityManager.findOne(...) or entityManager.save(...), it works just fine. But, if I do either entityManager.clear(Entity) or entityManager.query('DELETE FROM table'), those queries are being run outside the transaction, meaning they are taking effect even after I rollback.
Author   typeorm
🌐
GitHub
github.com › typeorm › typeorm › issues › 9719
Ordering not working/ relations not loading in query strategy within transactions · Issue #9719 · typeorm/typeorm
January 19, 2023 - const productAlias = "product" const queryBuilder = this.createQueryBuilder(productAlias) queryBuilder.expressionMap.relationLoadStrategy = "query" const options_ = { ...options } // ..other constraint and things happen here but does not concern this issue since the conditions are not meet const res = await queryBuilder.setFindOptions(options_).getManyAndCount()
Author   typeorm
🌐
Productsway
typeorm-legacy.productsway.com › transactions
Transactions - TypeORM 0.2.38
You can manually create a query runner instance and use it to manually control transaction state. Example: import {getConnection} from "typeorm"; // get a connection and create a new query runner const connection = getConnection(); const queryRunner = connection.createQueryRunner(); // establish ...