假设我们有2个服务A和B。 服务A具有执行以下功能的功能:
现在,让我们假设以下第3步或第4步之一失败了。 由于服务B在数据库中进行了更改,因此这些更改仍然存在。
在这种情况下,有什么办法可以回滚数据库?我虽然是关于数据库事务的,但是我在嵌套js中找不到任何方法可以做到这一点,尽管TypeOrm支持它,但是嵌套起来似乎并不自然。 如果没有,那么我现在就被服务B所发生的更改“卡住”了,但如果服务A发生了更改,我就不会被更改。
非常感谢。
答案 0 :(得分:2)
有许多解决方案,它们都应基于SQL事务管理。
我个人认为,最简单的方法是在数据库上执行代码时使用相同的EntityManager
实例。然后,您可以使用类似的内容:
getConnection().transaction(entityManager -> {
service1.doStuff1(entityManager);
service2.doStuff2(entityManager);
});
您可以从QueryRunner
实例中产生一个EntityManager
,该实例将包装在同一事务中,以防您在ORM操作之外执行原始SQL。您还需要从Repository
生成EntityManager
个实例,否则它们将在主事务之外执行代码。
答案 1 :(得分:2)
这是我解决问题的方法,因为我需要使用悲观锁。
我认为这是一种“巢式”的处理方式,您可以简单地要求NestJS
注入Typeorm Connection
的实例,那么您就很好了。
@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' },
}),
);
});
});
}
}
只需将所需的任何其他逻辑放入.transaction
块中,就可以了。
注意:您必须使用entityManager
方法提供的.transaction
,否则它将不起作用。
答案 2 :(得分:1)
typeorm-transactional-cls-hooked使用CLS(连续本地存储)来处理和传播不同存储库和服务方法之间的事务。
@Injectable()
export class PostService {
constructor(
private readonly authorRepository: AuthorRepository,
private readonly postRepository: PostRepository,
) {}
@Transactional() // will open a transaction if one doesn't already exist
async createPost(authorUsername: string, message: string): Promise<Post> {
const author = await this.authorRepository.create({ username: authorUsername });
return this.postRepository.save({ message, author_id: author.id });
}
}
答案 3 :(得分:0)
在这种情况下,两个数据库操作必须使用相同的事务管理器。不幸的是,我没有示例存储库,但是我发现了在Node:
中使用Continuation Local Storage(CLS)的潜在解决方案。https://github.com/typeorm/typeorm/issues/1895
这适用于Express.js,但您可以创建TransactionManager的实例(例如,在嵌套中间件中),并在每个请求上下文中存储它。然后,您可以在服务方法调用中重用此事务管理器,只要在上面的链接中使用@Transaction装饰器实现对其进行注释即可。
如果您的功能链中没有错误,则事务管理器将提交所有所做的更改。否则,经理将回滚所有更改。
希望这会有所帮助!