删除关联记录而不是使用 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
关联的记录 exercise1
和 exercise2
。当我执行 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方法,就没什么好担心的了。
我有以下联想:
Training.hasMany(Exercise, {
foreignKey: 'trainingId',
as: 'exercises',
});
Exercise.belongsTo(Training, {
foreignKey: 'trainingId',
as: 'training',
});
考虑到我有与记录 training
关联的记录 exercise1
和 exercise2
。当我执行 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方法,就没什么好担心的了。