删除关联记录而不是使用 sequelize 将 fk 设置为 null

Delete associated record instead of settings fk to null with sequelize

我有以下联想:

Training.hasMany(Exercise, {
  foreignKey: 'trainingId',
  as: 'exercises',
});

Exercise.belongsTo(Training, {
  foreignKey: 'trainingId',
  as: 'training',
});

考虑到我有与记录 training 关联的记录 exercise1exercise2。当我执行 training.setExercises([exercise2]) 时,我希望 exercise1 被删除并保留 exercise2,但是,sequelize 对 exercise1 进行更新以设置 trainingId 为空。不是应该删除记录吗?如果这是预期的行为,我如何让它删除记录而不是将 FK 更新为空?

是的,这是预期的行为。根据 the docs:

Set the associated models by passing an array of persisted instances or their primary keys. Everything that is not in the passed array will be un-associated

请注意,其他所有内容都会 un-associated,不会被删除。据我所知,除非您在表上配置了级联,否则没有任何关联功能会删除资源。

没有一个查询可以实现您想要的,但这样的查询应该足够了。如果您的数据库支持,建议您在事务中执行以下操作。

// get existing associated exercises
const exercises = await training.getExercises();

// associate the new exercise
await training.addExercise(exercise2);

// delete the previously associated exercises
for (let i = 0, n = exercises.length; i < n; i++)
{
    await exercises[i].destroy();
}

正确答案来自@ankh,但还有其他替代解决方案。我仍然想使用 set<recourse_name>s 方法的原因是它负责保留现有记录,它不会只是替换所有内容。我找到了两个解决方案,但首先,它需要实体中的 FK 被管理为具有 allowNull: false。这样,当使用 set<recourse_name>s 方法时,它将按预期取消设置(将 FK 设置为 null)并且不会引发异常。在此之后,您有两个选择:

首先是调用<managed_entity_model>.destroy({ where: { <fk_name>: null }})。这样做,您将删除所有未设置的记录。

根据问题中的示例编码:

// This will set the FK in the un-associated records to null
training.setExercises([exercise2]);

// This will delete the records that have the Fk set to null
Exercise.destroy({
  where: { trainingId: null },
});

二是在被管实体中设置触发器删除FK更新为null的记录。因此,当那里的方法将 FK 更新为 null 时,数据库将负责删除记录。与第一个解决方案相比的优势在于,这个解决方案只对数据库进行一次查询,并且不会发生更新,因为它在删除后被取消了。下面是此触发器的示例:

CREATE OR REPLACE FUNCTION delete_<managed_entity_table>_on_update_of_<fk_name>_to_null()
  RETURNS trigger AS
$func$
BEGIN
  IF NEW."<fk_name>" IS NULL THEN -- If the FK is set to null:
    DELETE FROM <managed_entity_table> WHERE id = OLD.id; -- Delete the record
    RETURN NULL; -- And cancel the update since the record is deleted already
  ELSE
    RETURN NEW;
  END IF;
END
$func$ LANGUAGE plpgsql;

CREATE TRIGGER delete_on_update_of_<fk_name>_to_null
BEFORE UPDATE OF "<fk_name>"
ON <managed_entity_table>
FOR EACH ROW
EXECUTE PROCEDURE delete_<managed_entity_table>_on_update_of_<fk_name>_to_null();

根据问题中的示例编码:

// The database will take care of deleting the un-associated records in
// the same query
training.setExercises([exercise2]);

任何这些解决方案的结果是未设置的记录将不存在。但要小心,因为 FK 设置为 notNull: false。如果不直接乱搞,关系一直用sequelize方法,就没什么好担心的了。