来自MongoDB世界,在我开始专业使用它之前,我对SQL的熟悉程度有限。作为一名学生,我有一些基本的想法,但是请相信我,这几乎没什么好说的!
当我回头看我过去写的与操作相关的代码时,我的内心自我嘲笑我。我希望我可以去过去并重写这些代码。
我和typeorm一起进入了SQL世界。作为经验丰富的MongoDB用户,我创建的方法类似于NOSQL功能。
让我们进入一个例子。让我们提供有关我们需要实施的内容的完整规范。
- 我们必须创建一个用户。 input payload包含电子邮件和角色ID。
- 我们需要确保用户的电子邮件是唯一的。对于已经分配的电子邮件,我们需要提出冲突例外。
- 我们需要确保请求者在尝试创建具有无效角色ID的用户时会收到无法处理的实体异常。为此,我们需要假设系统中的角色创建功能已在系统中可用。
非常简单,哈?
顺便说一句,什么是ORM(和Typeorm)?如果我介绍这些帖子的范围将增加。我建议检查What is an ORM by Ihechikara Vincent Abba和Typeorm的official documentation。
TypeOMM提供了一种简单的方法来快速创建数据模型,使我们能够在短短几分钟内创建表和数据库约束。
长话短说,我使用Nest JS框架创建了一个tiny project,它按照Typeorm方法设计了数据模型,并构建了Role和User表。 User
表中的roleId
列是Role
表的外键。此外,我使email
独特。
屏幕截图:DB的er图以及表的主要和外键
一切都设置了。现在,这就是创建接受输入有效载荷并将用户持续到DB的方法。
我的新手自我曾经像下面的那个编写代码一样,曾经假设我需要实现的一切,我需要自己做。
async saveUserV1(userSaveRequest: UserSaveRequest): Promise<string> {
await this.validateRole(userSaveRequest.roleId);
await this.validateEmail(userSaveRequest.email);
const insertResult = await this.userRepository.insert({
email: userSaveRequest.email,
role: {
id: userSaveRequest.roleId,
},
});
return insertResult.generatedMaps[0].id;
}
private async validateRole(roleId: string): Promise<void> {
const isRoleExist = await this.roleRepository.exist({ where: { id: roleId } });
if (!isRoleExist) {
throw new UnprocessableEntityException
(`Invalid role ID`);
}
}
private async validateEmail(email: string): Promise<void> {
const isUserWithEmailExist = await this.userRepository.exist({ where: { email: email } });
if (isUserWithEmailExist) {
throw new ConflictException(`Email is associated with another user`);
}
}
让我们审查代码。这是我的观察。
在SQL世界中,PostgreSQL之类的数据库自动处理验证。借助电子邮件字段上的唯一约束UQ_User_email
以及外国密钥FK_User_roleId_Role_id
强制执行的参考完整性,因此无需其他查询来验证数据。这些内置功能使手动验证冗余并减少到数据库中。
因此,作为拉的请求审稿人,我目前的自我将标记为PR的请求更改,并建议将验证责任委派给数据库。
好吧,我们需要重构代码。
我将利用数据库的内置错误处理功能。我的目标是处理异常,以便请求者获取正确的错误消息。
这是我更改的代码。
async saveUserV2(userSaveRequest: UserSaveRequest): Promise<string> {
try {
const insertResult = await this.userRepository.insert({
email: userSaveRequest.email,
role: {
id: userSaveRequest.roleId,
},
});
return insertResult.generatedMaps[0].id;
} catch (error) {
this.handleRepositoryException(error);
throw error;
}
}
private handleRepositoryException(repositoryException: RepositoryException): void {
const errorMap: Record<string, Error> = {
UQ_User_email: new ConflictException(`Email is associated with another user`),
FK_User_roleId_Role_id: new UnprocessableEntityException
(`Invalid role ID`),
};
throw errorMap[repositoryException.constraint];
}
非常有光泽,不是吗?不幸的是,我一直在使用SQL长时间手动验证所有内容的第一种方法。
顺便说一句,所有代码均在GitHub repository上可用。欢迎您克隆它并在本地运行。
总而言之,这种思维方式的转变使我能够拥抱SQL的力量并充分利用其功能,减少执行时间并简化代码。如果数据库可以为我们处理任务,为什么不完全依靠它?
感谢您阅读它。我希望你喜欢它。我想与所有人分享我的小型Typeorm和PostgreSQL经验;这是我的第一次尝试。
nb :我从here中拍摄了封面图像。