PostgreSQL:仅对源列的双向引用进行级联删除

xwbd5t1u  于 2023-03-17  发布在  PostgreSQL
关注(0)|答案(3)|浏览(142)

我有表variablevariable_variable,最后一个表包含2列:

  1. variable_id_from
  2. variable_id_from
    请看下面的模式:

如果variable_variable包含值为{variable_id_from = 1, variable_id_to = 2}的行,则意味着具有id = 1variable引用了具有id = 2variable
现在我有了一个新的业务需求:
假设我们有variable A,如果有任何其他variables引用A(即variable_variable有任何值为{variable_id_to = A.id}的行),则必须禁止删除A
假设我们有variable B,如果没有变量引用B,但同时B引用任何其他变量,则删除B应成功进行,并且删除{variable_id_from = B.id}的所有引用。
我想创建一个简单的约束和级联删除。看看下面的SQL片段:

ALTER TABLE variable_variable
    ADD CONSTRAINT variable_variable_variable_id_from_fkey
        FOREIGN KEY (variable_id_from) REFERENCES variable (id)
            ON DELETE CASCADE;

ALTER TABLE variable_variable
    ADD CONSTRAINT variable_variable_variable_id_from_fkey
        FOREIGN KEY (variable_id_to) REFERENCES variable (id);

我原以为它能完全满足我的需求,但奇怪的是,这个测试用例没有通过:
1.创建变量A
1.创建变量B
1.使A成为B的参考
1.尝试删除path2.net
1.预期:删除变量时出错。实际值Bvariable_variable中的所有对应链路已成功删除。
奇怪,好像variable_variable_variable_id_from_fkey被触发了,有什么办法解决这个问题吗?
另外还有一个重要的情况,变量可能会引用自己,那么variable_variable表可以包含行{variable_id_from = 1, variable_id_to = 1},这种情况下删除也应该成功通过,需要级联删除所有的链接。
另外,我知道我可以在应用程序端执行删除操作,但我认为这个决定是最后的选择。整个数据库结构比我展示的要复杂得多。级联约束确实有助于保持代码的整洁。

qvtsj1bj

qvtsj1bj1#

一个外键将与deleet上的加法级联,同时删除桥表中的所有记录
BEFORE DELETE TRIGGER将执行此操作,因为它将检查每个删除的行(如果variable_variable中有记录
参见示例
x一个一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零二年零一年零一年零一年零三年零一年零一年零一年零四年零一年零一年零一年零一年零六年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零三年零一年零一年零一年零一年零一年零一年零一年零一年零一年零三年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一年零一
| 身份证|姓名|
| - ------|- ------|
| 1个|测验|

SELECT 1

fiddle

svmlkihl

svmlkihl2#

感谢大家提出的解决方案。我设法解决了我的问题。下面是定义的约束:

-- If current variable is being referenced by another variable, restrict deletion
ALTER TABLE variable_variable
    ADD CONSTRAINT variable_variable_variable_id_to_fkey
        FOREIGN KEY (variable_id_to) REFERENCES variable (id)
            ON DELETE RESTRICT;

-- If current variable reference other variable, delete its link by cascade
ALTER TABLE variable_variable
    ADD CONSTRAINT variable_variable_variable_id_from_fkey
        FOREIGN KEY (variable_id_from) REFERENCES variable (id)
            ON DELETE CASCADE;

据我所知,PostgreSQL会按照创建约束的顺序来检查它们。所以,下面是发生的事情:
1.如果存在Other Variable -> Current Variable等参照,则限制删除,否则执行下一步。
1.如果有类似Current Variable -> Other Variable的引用,请删除级联上variable_variable表中的Current Variable及其链接。
但是,我遇到了另一个问题,不是约束问题,而是Hibernate问题,Variable实体定义如下:

@Entity
@Table(name = "variable")
@Getter
@Setter(PROTECTED)
@NoArgsConstructor(access = PROTECTED)
@DynamicUpdate
public class Variable {
    @EmbeddedId
    private VariableId id;

    @ManyToMany(fetch = LAZY)
    @JoinTable(
        name = "variable_variable",
        joinColumns = @JoinColumn(name = "variable_id_from"),
        inverseJoinColumns = @JoinColumn(name = "variable_id_to")
    )
    private Set<Variable> variables = new HashSet<>();

    @ManyToMany(fetch = LAZY)
    @JoinTable(
        name = "variable_variable",
        joinColumns = @JoinColumn(name = "variable_id_to", updatable = false, insertable = false),
        inverseJoinColumns = @JoinColumn(name = "variable_id_from", updatable = false, insertable = false)
    )
    // this collection is readonly and never updates
    private Set<Variable> inverseVariables = new HashSet<>();
    
    ...
}

我曾经使用简单的Spring Data JPA存储库方法delete(Variable variable)删除Variable,实际上,这是生成的查询:

delete from variable_variable where variable_id_from = ?
delete from variable_variable where variable_id_to = ?
delete from variable where id = ?

据我所知,拥有方的ManyToMany集合总是orphanRemoval = true,因此Hibernate总是在删除实体本身之前删除ManyToMany链接(如果我错了请纠正我),因此DB约束毫无意义,因为Hibernate过早地删除了所有链接。
现在,我使用原生SQL查询,并标记deletedeleteById方法以抛出UnsupportedOperationException,这样就不会有人意外调用它们。无论如何,我不认为这是一个清晰的解决方案。您知道如何让Hibernate不要删除所有者端的ManyToMany链接吗?

a5g8bdjr

a5g8bdjr3#

您面临的问题是,删除变量B时,variable_id_to上的外键约束条件variable_variable_id_from_fkey不会被触发,因为它不是使用ON DELETE CASCADE定义的。这意味着variable_variable中引用B的行不会被删除,因此,B的删除将成功进行。
要解决此问题,可以修改variable_id_to上的外键约束条件,使其也具有ON DELETE CASCADE:

ALTER TABLE variable_variable
    ADD CONSTRAINT variable_variable_variable_id_to_fkey
        FOREIGN KEY (variable_id_to) REFERENCES variable (id)
            ON DELETE CASCADE;

通过此修改,当您删除变量B时,variable_variable中引用B的所有行(即,variable_id_to等于B的id)也将被删除。这将防止在任何其他变量引用B时删除B。
要处理变量引用自身的情况,可以向变量表添加一个触发器,该触发器检查变量是否引用自身并删除variable_variable中的相应行:

CREATE TRIGGER delete_self_reference
    BEFORE DELETE ON variable
    FOR EACH ROW
    BEGIN
        DELETE FROM variable_variable WHERE variable_id_from = OLD.id AND variable_id_to = OLD.id;
    END;

从变量表中删除行之前将触发此触发器。如果要删除的行的ID出现在variable_variable中某行的两列中,则将删除variable_variable中的相应行。这将允许删除变量,即使该变量引用自身。
通过这些修改、外键约束和触发器,您应该能够满足业务需求并保持数据库结构清晰。
想一想

相关问题