在单个事务中执行一系列查询时出现 QueryRunnerAlreadyReleasedError

Han*_*tsy 7 typeorm nestjs

我添加了一项OnMoudleInit服务来初始化 NestJS 应用程序中的一些示例数据。

TypeORM 提供了多种将查询包装到单个事务中的方法。

我尝试使用EntityManager.transaction来包装操作。

 await this.manager.transaction(async (manager) => {
      // NOTE: you must perform all database operations using the given manager instance
      // it's a special instance of EntityManager working with this transaction
      // and don't forget to await things here
      const del = await manager.delete(PostEntity, {});
      console.log('posts deleted: ', del.affected);

      const userDel = await manager.delete(UserEntity, {});
      console.log('users deleted: ', userDel.affected);

      const user = new UserEntity();
      Object.assign(user, {
        firstName: 'hantsy',
        lastName: 'bai',
        email: 'hantsy@gmail.com',
      });
      const savedUser = await manager.save(user);
      console.log('saved user: ', JSON.stringify(savedUser));
      this.data.forEach(async (d) => {
        const p = new PostEntity();
        p.author = savedUser;
        
        // comment out these relation settings it will work well.
        // 
        // const comment = new CommentEntity();
        // comment.content = 'test comment at:' + new Date();
        // p.comments = Promise.resolve([comment]);
        Object.assign(p, d);
        await manager.save(p);
      });
    });

    const savedPosts = await this.postRepository.find({});
    console.log('saved:', JSON.stringify(savedPosts));
  }
Run Code Online (Sandbox Code Playgroud)

当应用程序启动时,出现以下错误。

posts deleted:  2
users deleted:  1
saved user:  {"firstName":"hantsy","lastName":"bai","email":"hantsy@gmail.com","id":"04d5cc63-d36a-4d80-a37f-97424ef168a8"}
D:\hantsylabs\nestjs-graphql-sample\node_modules\typeorm\error\QueryRunnerAlreadyReleasedError.js:10
        var _this = _super.call(this) || this;
                           ^

QueryRunnerAlreadyReleasedError: Query runner already released. Cannot run queries anymore.
    at new QueryRunnerAlreadyReleasedError (D:\hantsylabs\nestjs-graphql-sample\node_modules\typeorm\error\QueryRunnerAlreadyReleasedError.js:10:28)
Run Code Online (Sandbox Code Playgroud)

更新:我发现这是由帖子/评论cascade设置引起的,我试图使用一个命令来保存帖子/评论。

  @OneToMany((type) => CommentEntity, (comment) => comment.post, {
    cascade: true,
  })
  comments?: Promise<CommentEntity[]>;

Run Code Online (Sandbox Code Playgroud)

更新 2:如果我使用该类Repository来执行save任务,它似乎有效。

    const post = new PostEntity();
    post.title = 'test title';
    post.content = 'test content';
    const comment = new CommentEntity();
    comment.content = 'test comment';
    post.comments = Promise.resolve([comment]);
    await this.postRepository.save(post);
    //console.log('saved from repository: ', JSON.stringify(savedPost));
Run Code Online (Sandbox Code Playgroud)

当我在管理器事务块之前添加上述代码时,我发现manager.delete(Post, {})没有应用cascade设置?

Han*_*tsy 12

从 Nestjs/TypeORM 不和谐讨论中得到答案。

将以下代码更改为:

this.data.forEach(async (d) => {
        const p = new PostEntity();
        p.author = savedUser;
        
        // comment out these relation settings it will work well.
        // 
        // const comment = new CommentEntity();
        // comment.content = 'test comment at:' + new Date();
        // p.comments = Promise.resolve([comment]);
        Object.assign(p, d);
        await manager.save(p);
      });
Run Code Online (Sandbox Code Playgroud)

为此,使用Promise.all包装所有异步代码。

      await Promise.all(
        this.data.map(async (d) => {
          const p = new PostEntity();
          Object.assign(p, d);
          p.author = user;

          const c = CommentEntity.of('test comment at:' + new Date());
          p.comments = Promise.resolve([c]);
          await mgr.save(p);
        }),
      );
    });
Run Code Online (Sandbox Code Playgroud)

  • @AfsharMohebi 的方法如下:在第一个示例中,`forEach` 触发所有异步函数,代码继续执行,最终退出函数和事务。manager.save(p) 方法可能有一些承诺链,在交易结束后很长一段时间内执行。另一方面,“await Promise.all”不会让执行恢复,直到传递给 Promise.all 的所有承诺都得到解决。您还可以实现类似(但顺序)的结果,如下所示:`for (const d of this.data) {await manager.save(new PostEntity())}` (4认同)