Aliheym / typeorm-transactional

A Transactional Method Decorator for TypeORM that uses Async Local Storage or cls-hooked to handle and propagate transactions between different repositories and service methods.
MIT License
201 stars 27 forks source link

@Transactional didn't work with the save method. #7

Closed AbdulBari5 closed 1 year ago

AbdulBari5 commented 2 years ago

Hello, I was exploring this library. The work is great. There was a little issue that I faced during implementing this package was that on calling the save method of repository, it opens its own transaction scope and the query didn't execute in the scope opened by @transactional decorator. For this purpose I used the getEntityManagerInContext() function to retrieve the entity manager and call the save method using it. There's a way to stop the save method opening it's own transaction scope by passing an options.transaction = false as a second argument. But I don't like doing it in every function that's using the @Transactional. I have solved my problem my getting the entity manager from the getEntityManagerInContext() function and doing all the db operations using that entity manager.Since the package currently doesn't export it, so If you could just export it from the package in order to retrieve the entity manger in context easily.

Aliheym commented 2 years ago

Hi @AbdulBari5. Could you please prepare a small example for your use case?

I know that when using the save method, TypeORM begins a transaction and we can manage this with the transaction option, but if you use typeorm-transactional correctly, TypeORM should not do this. TypeORM checks that the transaction already exists and don't open a new one.

For example, we have this method:

  async saveEntity(name: string) {
    await this.repository.save({ name });

    const entity = await this.repository.findOneBy({ name });

    return entity;
  }

We don't specify the Transactional decorator here, so TypeORM begins transaction only for the save method and we will have the next queries:

query: START TRANSACTION // transaction begins only for insert operation
query: INSERT INTO "users"("name") VALUES ($1) RETURNING "id" -- PARAMETERS: ["test"]
query: COMMIT // end
query: SELECT "UserEntity"."id" AS "UserEntity_id", "UserEntity"."name" AS "UserEntity_name" FROM "users" "UserEntity" WHERE ("UserEntity"."name" = $1) LIMIT 1 -- PARAMETERS: ["test"]

Let's try this example with this library and Transactional decorator:

  @Transactional()
  async saveEntity(name: string) {
    await this.repository.save({ name });

    const entity = await this.repository.findOneBy({ name });

    return entity;
  }

We will get another result:

query: START TRANSACTION  // transaction begins only for both operations: insert and select
query: INSERT INTO "users"("name") VALUES ($1) RETURNING "id" -- PARAMETERS: ["test"]
query: SELECT "UserEntity"."id" AS "UserEntity_id", "UserEntity"."name" AS "UserEntity_name" FROM "users" "UserEntity" WHERE ("UserEntity"."name" = $1) LIMIT 1 -- PARAMETERS: ["test"]
query: COMMIT // end

So, normally it should work with the save method. Could you please give more details or prepare small example with your use case?

Aliheym commented 1 year ago

Feel free to reopen issue