TypeORM如何判断字段是否变更 仅密码修改时重新哈希密码
解决方案
方案1:使用实体生命周期钩子(推荐,适配ActiveRecord模式)
这种方案不需要引入额外组件,直接在现有User实体上扩展即可,逻辑等价于你在Mongoose中使用的isModified判断:
import { Entity, PrimaryGeneratedColumn, Column, BeforeInsert, BeforeUpdate, AfterLoad } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() public id: number; @Column() public username: string; @Column() public password: string; // 运行时临时存储原始密码,不持久化到数据库 private oldPassword: string; // 实体从数据库加载完成后,记录原始密码 @AfterLoad() public saveOriginalPassword() { this.oldPassword = this.password; } @BeforeInsert() public async hashPassword() { this.password = await hashPassword(this.password); } // 更新前判断密码是否变更,仅变更时重新哈希 @BeforeUpdate() public async hashPasswordIfChanged() { if (this.password !== this.oldPassword) { this.password = await hashPassword(this.password); } } }
注意:
oldPassword为纯内存运行时字段,不会写入数据库,不存在服务宕机丢失数据的风险,即使服务中断未完成更新,也不会影响数据库中已存储的合法数据。
方案2:使用TypeORM Subscriber
如果你希望将实体监听逻辑和实体定义解耦,可以使用Subscriber方案:
步骤1:创建Subscriber类
import { EventSubscriber, EntitySubscriberInterface, InsertEvent, UpdateEvent } from 'typeorm'; import { User } from './user.entity'; @EventSubscriber() export class UserSubscriber implements EntitySubscriberInterface<User> { // 指定监听的实体 listenTo() { return User; } // 插入前哈希密码 async beforeInsert(event: InsertEvent<User>) { event.entity.password = await hashPassword(event.entity.password); } // 更新前判断密码是否变更 async beforeUpdate(event: UpdateEvent<User>) { // 判断本次更新是否包含password字段 const isPasswordUpdated = event.updatedColumns.some(column => column.propertyName === 'password'); if (isPasswordUpdated) { event.entity.password = await hashPassword(event.entity.password); } } }
步骤2:注册Subscriber
在TypeORM配置文件中添加Subscriber注册配置,以ormconfig.ts为例:
import { UserSubscriber } from './subscribers/user.subscriber'; export default { // 其他数据库配置... entities: [User], subscribers: [UserSubscriber] }
注意事项
- 两种方案默认仅对
repository.save()触发的单实体插入/更新生效,如果使用repository.update()、QueryBuilder等批量操作接口,生命周期钩子和Subscriber不会自动触发,需要手动处理密码哈希逻辑。 - 如果使用方案1,更新实体时需要先从数据库查询出实体实例,修改后再调用
save方法,才能触发AfterLoad和BeforeUpdate钩子。
内容的提问来源于stack exchange,提问作者Skyler Brandt
相关产品推荐
相关产品推荐

