TypeORM 技巧(第一部分:不要使用 save())
系列简介
截至 2022 年 1 月 21 日(来源),TypeORM 是第三流行的Javascript ORM 库,如果我们谈论的是 Typescript,它无疑是最受欢迎的。
过去两年来,我一直在深入使用这个库,每天用它处理数百万次数据库查询。在本系列文章中,我将列出我在使用这个项目过程中学到的一些技巧和陷阱,这些技巧和陷阱帮助我在生产环境中捕获错误并优化 API 调用。我会尽量保持每篇文章简短、信息丰富且通俗易懂,以便您能够在代码中快速运用这些优化方法。
在每篇文章中,我们将讨论:
- 问题是什么?
- 为什么是错的?
- 你怎么能修复它?
那么让我们开始吧!
save() 与 insert()、update()
存储库具有该.save()
方法,该方法具有双重功能:
- 如果实体尚不存在,则插入该实体。
- 如果实体存在,它会尝试更新现有实体。
让我们观察一下该方法的两个示例用法:
以下是从注册新用户的 API 端点获取的代码片段:
const user = this.userRepository.create({
name: "Rishit",
password: "test123",
});
await this.userRepository.save(user);
以下是来自另一个端点的片段,用于更新现有用户的名称:
const user = this.userRepository.findOne({
name: "Rishit",
});
user.name = "John";
await this.userRepository.save(user);
很方便吧?其实也没那么方便,因为性能会有所下降。 的实现save()
会执行两个查询,而不是一个:
- 首先,它使用
SELECT
查询来搜索现有实体。 - 如果步骤 1 返回一条记录,则用于
UPDATE
更新该记录。否则,用于INSERT
插入一条新记录。
为什么不好?
- 这些查询需要两次往返数据库,这意味着您必须忍受每次往返造成的网络延迟。
- 您的代码中存在特定的上下文,您确实知道您正在插入/更新,并且不需要使用
save()
的双重功能。 - TypeORM 生成的查询
SELECT
包含子查询,这对于具有数百万行的表来说效率极低。
如何修复这个问题?
查看使用查询的上下文,通常可以让你决定是要执行insert()
还是update()
。例如,你可以将上面两个代码片段重构为:
注册新用户:
const user = this.userRepository.create({
name: "Rishit",
password: "test123",
});
await this.userRepository.insert(user);
更新现有用户:
await this.userRepository.update({
name: "Rishit",
},{
name: "John",
});
就这样,您将这些查询导致的数据库负载减少了一半:性能提升了2 倍! 的用法可能看起来很明显。尽管如此,由于TypeORM 本身的文档推荐它作为更新实体的主要模式,save()
因此它仍然是代码中普遍使用的做法。.save()
话虽如此,这确实为需要根据实体是否存在进行“或”save()
操作的代码提供了一个有效的用例。然而,我确信大多数用例并不需要这个功能,它们只想插入或更新记录,而不是两者兼而有之。insert
update