TypeORM一对多/多对一关联更新时设为null或不生效问题
Stage截止日期调整时子Task移动异常问题
已查阅所有能找到的类似帖子,所有解决方案均无效,可能是操作有误,特此求助。
功能说明
项目中存在Stage(阶段)、Task(任务)和Checkpoint(检查点),三者均有独立截止日期。一个Stage包含多个Task和Checkpoint,Task有时会关联Checkpoint。
此前无法灵活修改Stage的截止日期:若要延后Stage截止日期,必须将所有子Task和Checkpoint一并后移,否则它们将不再属于该Stage。
实体关联代码
@Entity() class Program { @PrimaryGeneratedColumn() id: string; @OneToMany(() => Stage, (stage) => stage.program) stages: Promise<Stage[]>; } @Entity() class Stage { @PrimaryGeneratedColumn() id: string; @OneToMany(() => Task, (task) => task.stage) tasks: Promise<Task[]>; @OneToOne(() => Checkpoint, (checkpoint) => checkpoint.stage, { eager: true }) @JoinColumn() finalCheckpoint: Checkpoint; @OneToMany(() => Checkpoint, (checkpoint) => checkpoint.stage) intermediateCheckpoints: Promise<Checkpoint[]>; } @Entity() class Checkpoint { @PrimaryGeneratedColumn() id: string; @ManyToOne(() => Stage, (stage) => stage.intermediateCheckpoints, { eager: true }) @JoinColumn() stage: Stage; } @Entity() class Task { @PrimaryGeneratedColumn() id: string; @ManyToOne(() => Stage, (stage) => stage.tasks, { eager: true }) @JoinColumn() stage: Stage; @OneToOne(() => Checkpoint, { nullable: true }) @JoinColumn() checkpoint: Checkpoint; }
目标
实现Stage截止日期可前后调整超出原边界,子实体(Task和Checkpoint)可自动移动到符合新截止日期的Stage,即更新stageId到对应Stage。
现有代码流程
首先调用updateStage函数,满足条件时调用moveStageChildrenWithoutLimits移动子实体:
export async function updateStage( transactionalEntityManager: EntityManager, programId: string, stageId: string, authInfo: AuthorizationInfoDto, req: StageUpdate, ) { const program = await retrieveProgram(transactionalEntityManager, programId); const stage = await retrieveStage(transactionalEntityManager, stageId, programId); // joins the tasks and checkpoints if (req.forceUpdateDeadline && req.deadline) { await moveStageChildrenWithoutLimits( transactionalEntityManager, program, stage, req.deadline, ); // move tasks and checkpoints to the appropriate stage } stage.deadline = req.deadline; const updatedStage = await transactionalEntityManager.save(stage); return await toStageDto(updatedStage, authInfo); }
移动子实体的核心函数:
async function moveStageChildrenWithoutLimits( transactionalEntityManager: EntityManager, program: Program, stage: Stage, newDeadlineReq: Date, ) { const stages = await program.stages; const stageNewDeadline = new Date(newDeadlineReq).getTime(); const stageOldDeadline = new Date(stage.deadline).getTime(); const stageIndex = stages.findIndex((s) => s.id === stage.id); // 新截止日期早于原日期 if (stageNewDeadline < stageOldDeadline) { const tasks = await stage.tasks; const tasksToMove = tasks.filter( (task) => task.deadline.getTime() > stageNewDeadline, ); const tasksToUnlink = tasks.filter( (task) => task.deadline.getTime() < stageNewDeadline && task.checkpointId, ); const intermediateCheckpoints = await stage.intermediateCheckpoints; const intermediateCheckpointsToMove = intermediateCheckpoints.filter( (checkpoint) => checkpoint.deadline.getTime() > stageNewDeadline && stage.finalCheckpoint.id !== checkpoint.id, ); const nextStage = stages[stageIndex + 1]; // 将任务移动到下一个阶段 const taskPromises = tasksToMove.map(async (task) => { task.stage = nextStage; return transactionalEntityManager.getRepository(Task).save(task); }); await Promise.all(taskPromises); // 解除与下一阶段检查点关联的任务 const tasksUnlinked = tasksToUnlink.map(async (task) => { const linkedCheckpointRelevance = task.checkpointId; const checkpointRelevanceDB = await transactionalEntityManager .getRepository(CheckpointRelevance) .findOne({ where: { id: linkedCheckpointRelevance }, relations: ['checkpoint'], }); const checkpointDeadline = checkpointRelevanceDB.checkpoint.deadline.getTime(); if (checkpointDeadline > stageNewDeadline) { task.checkpoint = null; transactionalEntityManager .getRepository(CheckpointRelevance) .softDelete({ id: linkedCheckpointRelevance, }); return transactionalEntityManager .getRepository(Task) .save(task); } }); await Promise.all(tasksUnlinked); // 将中间检查点移动到下一阶段 const checkpointPromises = intermediateCheckpointsToMove.map( async (checkpoint) => { checkpoint.stage = nextStage; await transactionalEntityManager .getRepository(Checkpoint) .save(checkpoint); }, ); await Promise.all(checkpointPromises); } // 新截止日期晚于原日期 if (stageNewDeadline > stageOldDeadline) { const nextStage = stages[stageIndex + 1]; const tasks = await nextStage.tasks; const tasksToMove = tasks.filter( (task) => task.deadline.getTime() < stageNewDeadline, ); const intermediateCheckpoints = await nextStage.intermediateCheckpoints; const intermediateCheckpointsToMove = intermediateCheckpoints.filter( (checkpoint) => checkpoint.deadline.getTime() < stageNewDeadline && nextStage.finalCheckpoint.id !== checkpoint.id, ); const tasksPromise = tasksToMove.map(async (task) => { // 问题点:此处设置后数据库stageId变为null,完全不生效 task.stage = stage; const linkedCheckpointRelevance = task.checkpointId; if (linkedCheckpointRelevance) { const checkpointRelevanceDB = await transactionalEntityManager .getRepository(CheckpointRelevance) .findOne({ where: { id: linkedCheckpointRelevance }, relations: ['checkpoint'], }); const checkpointDeadline = checkpointRelevanceDB.checkpoint.deadline.getTime(); if (checkpointDeadline > stageNewDeadline) { task.checkpoint = null; transactionalEntityManager .getRepository(CheckpointRelevance) .softDelete({ id: linkedCheckpointRelevance, }); } } return transactionalEntityManager.getRepository(Task).save(task); }); await Promise.all(tasksPromise); const checkpointsPromise = intermediateCheckpointsToMove.map( async (checkpoint) => { checkpoint.stage = stage; return transactionalEntityManager .getRepository(Checkpoint) .save(checkpoint); }, ); await Promise.all(checkpointsPromise); } }
问题描述
当Stage新截止日期晚于原日期时,将下一个Stage的Task移动到当前Stage时,执行task.stage = stage后,数据库中的stageId被设置为null,完全不生效。
已尝试方案
- 使用QueryBuilder更新无效
- 使用raw sql的
.query()方法更新无效,但直接在数据库执行相同查询有效 - 尝试在Stage实体上添加新Task,同样无效
版本信息
- typeorm v0.3.20(从v0.3.16升级而来)
- postgres: v14.1 - alpine
- node v18.16
内容的提问来源于stack exchange,提问作者Edmund1645
相关产品推荐
相关产品推荐

