web-dev-qa-db-fra.com

Transaction de base de données nestjs / TypeOrm

En supposant que nous ayons 2 services, A et B. Le service A a une fonction faisant ce qui suit:

  1. Valider les données
  2. Appeler une fonction de service B qui modifie la base de données
  3. Faites plus de trucs
  4. Apportez des modifications à la base de données

Supposons maintenant que l'une des étapes suivantes, les étapes 3 ou 4, a échoué. Étant donné que le service B a apporté des modifications à la base de données, ces modifications sont toujours là.

Existe-t-il un moyen de restaurer la base de données dans ce cas? J'ai pensé aux transactions de base de données, mais je n'ai trouvé aucun moyen de le faire dans nest js, bien qu'il soit pris en charge par TypeOrm, il ne semble pas naturel d'imbriquer. Sinon, je suis maintenant "bloqué" avec les changements survenus par le service B, mais sans les changements auraient dû se produire par A.

Merci beaucoup.

5
superuser123

De nombreuses solutions sont disponibles, elles devraient toutes être basées sur la gestion des transactions SQL.

Personnellement, je pense que la façon la plus simple d'y parvenir est d'utiliser la même instance EntityManager lorsque vous exécutez du code sur votre base de données. Ensuite, vous pouvez utiliser quelque chose comme:

getConnection().transaction(entityManager -> {
    service1.doStuff1(entityManager);
    service2.doStuff2(entityManager);
});

Vous pouvez générer une QueryRunner à partir d'une instance EntityManager qui sera encapsulée dans la même transaction si vous exécutez du SQL brut en dehors des opérations ORM. Vous devez également générer Repository instances à partir de EntityManager ou ils exécuteront du code en dehors de la transaction principale.

2
zenbeni

Voici comment je l'ai résolu car j'avais besoin d'utiliser un verrou pessimiste.

Je pense que c'est la façon "Nest" de faire les choses car vous pouvez simplement demander à NestJS d'injecter une instance d'un Typeorm Connection et vous êtes prêt à partir.

@Injectable()
class MyService {
  // 1. Inject the Typeorm Connection
  constructor(@InjectConnection() private connection: Connection) { }

  async findById(id: number): Promise<Thing> {
    return new Promise(resolve => {
      // 2. Do your business logic
      this.connection.transaction(async entityManager => {
        resolve(
          await entityManager.findOne(Thing, id, {
            lock: { mode: 'pessimistic_write' },
          }),
        );
      });
    });
  }
}

Placez simplement toute autre logique dont vous avez besoin dans le .transaction bloquer et vous êtes prêt à partir.

REMARQUE: Vous DEVEZ utiliser le entityManager fourni par le .transaction méthode sinon cela ne fonctionnera pas.

1
nathanl93

Dans ce cas, vous devez utiliser le même gestionnaire de transactions pour les deux opérations de base de données. Malheureusement, je n'ai pas d'exemple de référentiel, mais j'ai trouvé une solution potentielle en utilisant Continuation Local Storage (CLS) dans Node:

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

Cela s'applique à Express.js, mais vous pouvez créer une instance de TransactionManager (par exemple, dans un middleware imbriqué) et la stocker pour chaque contexte de demande. Vous pourrez ensuite réutiliser ce gestionnaire transactionnel dans vos appels de méthode de service, à condition qu'ils soient annotés avec l'implémentation de décorateur @Transaction dans le lien ci-dessus.

S'il n'y a aucune erreur dans votre chaîne de fonctions, le gestionnaire de transactions valide toutes les modifications apportées. Sinon, le gestionnaire annulera toutes les modifications.

J'espère que cela t'aides!

0
ovidiup