Nest.js+TypeORM中beforeSoftRemove钩子无法更新数据库条目求助
Nest.js + TypeORM 全局订阅者实现软删除自动填充deletedById字段
问题背景
在Nest.js + TypeORM应用中,通过全局订阅者实现实体创建、更新、软删除时自动填充createdById、updatedById、deletedById字段,其中beforeInsert和beforeUpdate功能正常,但beforeSoftRemove钩子触发后,deletedById字段无法更新到数据库,使用的软删除方法为await this.todoRepository.softRemove(todoObject);。
问题原因
TypeORM的softRemove方法默认仅处理@DeleteDateColumn标记的deletedAt字段更新,beforeSoftRemove钩子中直接修改event.entity的自定义字段不会被自动纳入软删除的SQL更新语句,因此无法同步到数据库。
解决方案
在beforeSoftRemove钩子中,通过事件提供的queryRunner手动执行SQL语句,更新目标实体的deletedById字段。
修改后的全局订阅者代码
import { EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent, EventSubscriber } from 'typeorm'; @EventSubscriber() export class GlobalSubscriber implements EntitySubscriberInterface { beforeInsert(event: InsertEvent<any>): void { if (!event.entity.createdById) { // 替换为从请求上下文获取的真实用户ID event.entity.createdById = 'current-user-id'; } } beforeUpdate(event: UpdateEvent<any>): void { if (!event.entity.updatedById) { // 替换为从请求上下文获取的真实用户ID event.entity.updatedById = 'current-user-id'; } } async beforeSoftRemove(event: RemoveEvent<any>): Promise<void> { const targetEntity = event.entity; // 仅当实体未设置deletedById且存在主键ID时执行更新 if (!targetEntity.deletedById && targetEntity.id) { await event.queryRunner.query( `UPDATE "${event.metadata.tableName}" SET "deletedById" = $1 WHERE "id" = $2`, ['current-user-id', targetEntity.id] ); } } }
关键补充说明
- 获取真实用户ID:示例中的
'current-user-id'需要替换为实际业务中从请求上下文获取的用户ID,可通过Nest.js的AsyncLocalStorage实现全局请求上下文共享,避免订阅者依赖请求对象。 - 订阅者注册:确保在应用模块的TypeORM配置中注册该订阅者:
@Module({ imports: [ TypeOrmModule.forRoot({ // 数据库连接配置 subscribers: [GlobalSubscriber], // 添加全局订阅者 }), ], }) export class AppModule {}
- 替代方案(可选):若统一使用
softDelete方法(如await this.todoRepository.softDelete(todoId);),可在beforeUpdate钩子中判断是否为软删除操作(检测deletedAt字段是否被设置),自动填充deletedById,但需确保所有软删除操作均使用softDelete。
内容的提问来源于stack exchange,提问作者Muhammad Zeeshan
相关产品推荐
相关产品推荐

