TypeORM中软删除父实体后如何避免子实体被软删并正常查询
问题根源与解决步骤
首先,你添加的onDelete和orphanedRowAction配置没生效,是因为没放在关联装饰器的选项参数里。另外,TypeORM的软删除(通过@DeleteDateColumn)本身不会自动级联软删子实体——你看到的子实体"被软删",大概率是查询时被关联的父实体软删过滤器给过滤掉了,而非子实体真的被标记了deletedAt。
1. 修正实体关联配置
把数据库级别的关联配置放到@OneToMany和@ManyToOne的第三个参数里:
class Parent extends BaseEntity{ @Column() name:string @OneToMany( () => Child, (child) => child.parent, { onDelete: "NO ACTION", // 硬删父实体时,数据库不做级联操作(软删不触发这个) orphanedRowAction: "nullify" // 硬删父实体时,将子实体的parent外键设为null } ) children: Child[] @DeleteDateColumn() deletedAt?: Date; } class Child extends BaseEntity{ @Column() name:string @ManyToOne( () => Parent, (parent) => parent.children, { onDelete: "NO ACTION" // 对应父实体的配置,硬删时不级联 } ) parent: Parent }
2. 确保软删除父实体的方式正确
软删除父实体必须用softDelete或softRemove方法,不能用delete或remove(后者是硬删除,会触发数据库的onDelete规则):
// 正确的软删方式 await Parent.softDelete(parentId); // 或者 const parent = await Parent.findOneBy({ id: parentId }); await Parent.softRemove(parent);
3. 查询子实体时,避免被父实体的软删过滤器过滤
默认情况下,当你关联查询子实体和父实体时,TypeORM会自动过滤掉已软删的父实体,但子实体本身并没有被软删。如果要查询所有子实体(无论父实体是否被软删),可以用以下两种方式:
方式一:用find方法
const children = await Child.find({ relations: ["parent"], // 允许加载已软删的父实体 withDeleted: { parent: true } });
方式二:用createQueryBuilder
const children = await getConnection() .createQueryBuilder(Child, "child") // 左连接父实体,不自动过滤软删的父实体 .leftJoinAndSelect("child.parent", "parent") // 如果子实体本身没有软删字段,可去掉这行 .where("child.deletedAt IS NULL") .getMany();
这样操作后,软删父实体时,子实体的deletedAt不会被修改,查询子实体也能正常返回,不受父实体软删状态影响。
内容的提问来源于stack exchange,提问作者Amir Makram
相关产品推荐
相关产品推荐

