I add an OnMoudleInit
service to initialize some sample data in my NestJS application.
TypeORM provides several approaches to wrap the queries into a single transcation.
I tried to use EntityManager.transaction
to wrap the operations.
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));
}
When the application is starting up, the following error occurred.
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)
Update: I found this is caused by the post/comment cascade
settings, I was trying to use one command to save post/comments.
@OneToMany((type) => CommentEntity, (comment) => comment.post, {
cascade: true,
})
comments?: Promise<CommentEntity[]>;
Update 2: If I use the Repository
class to execute the save
task, it seems it works.
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));
When I added the above codes before the manager transaction block, and I found the manager.delete(Post, {})
did not apply the cascade
settings?
Got the answer from Nestjs/TypeORM discord discusstion.
Chnage the following codes to:
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);
});
to this, use Promise.all
to wrap all async codes.
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);
}),
);
});