我有这样的数据库表:
这些是外键约束:
-- 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;
所以,如果我有这样的参考资料:
Other Variable -> Current Variable
,那么Current Variable
就得禁止删除Current Variable -> Other Variable
,那么Current Variable
删除也应该删除级联上从Current Variable
到Other Variable
的链接(链接存储在variable_variable
表中)。这是我的
variable
表的 Hibernate 实体。
@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<>();
...
}
所以,
ManyToMany
关系在拥有方面,我没有CascadeType.REMOVE
。
但是如果我调用EntityManager.remove(variable)
,那些SQL语句被调用:
Query:["delete from variable_variable where variable_id_to=?"]
Params:[(7fbbf360-74a0-48db-a1e2-d3b1ab0a869f)]
Query:["delete from variable_variable where variable_id_from=?"]
Params:[(7fbbf360-74a0-48db-a1e2-d3b1ab0a869f)]
Query:["delete from variable where id=?"]
Params:[(7fbbf360-74a0-48db-a1e2-d3b1ab0a869f)]
Hibernate 过早地从
variable_variable
中删除链接。但是,这不是我需要的行为。它打破了我之前描述的 variable_variable
表中复杂数据库约束的整个想法。所以,我不希望 Hibernate 从那里删除任何东西。
我设法用原生查询解决了这个问题:
em.createNativeQuery("DELETE FROM variable WHERE id = :id")
.setParameter("id", variableId)
.executeUpdate();
无论如何,我不想介绍原生查询。是否可以告诉 Hibernate 不要删除
variable_variable
行删除Variable
?
我设法用
mappedBy
逆向引用解决了这个问题。看下面的代码片段:
@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, mappedBy = "variables")
// this collection is readonly and never updates
private Set<Variable> inverseVariables = new HashSet<>();
...
}
现在当我调用
em.remove(variable)
时,生成了那些SQL语句:
Query:["delete from variable_variable where variable_id_from=?"]
Params:[(7fbbf360-74a0-48db-a1e2-d3b1ab0a869f)]
Query:["delete from variable where id=?"]
Params:[(7fbbf360-74a0-48db-a1e2-d3b1ab0a869f)]
只有
Current Variable
和引用Current Variable -> Other Variable
被删除。如果有引用 Other Variable -> Current Variable
,则由于违反数据库完整性而导致删除失败。正是我需要的