我有这些实体
class Foo{
Set<Bar> bars;
}
class Bar{
Foo parent;
String localIdentifier;
}
通过这种映射(对不起,没有注释,我已经过时了):
<class name="Foo">
...
<set name="bars" cascade="all-delete-orphan" lazy="false" inverse="true">
<key>...</key>
<one-to-many class="Bar"/>
</set>
</class>
<class name="Bar">
...
<property name="localIdentifier" column="local_identifier"/>
<many-to-one name="parent" column="parent_id" />
</class>
我在2列上也有一个唯一约束:local_identifier
和parent_id
(不是每个列上的唯一约束,而是包含两者的唯一约束,例如没有2行具有相同的父和相同允许使用localIdentifier)
alter table bar add constraint unique_bar unique (parent_id, local_identifier)
这个使用它们的代码:
//foo is persistent, foo id = 1
Bars bars = foo.getBars();
bars.clear(); // bars contained 1 item [parent_id = 1, local_identifier = "a"]
Bar newBar = new Bar();
newBar.setParent(foo);
newBar.setLocalIdentifier("a");
bars.add(newBar);
现在,由于某种原因,Hibernate不会按照调用顺序执行任务。它不会在clear()
(插入)之前执行add()
(删除),反之亦然,它首先尝试插入,获取ConstraintViolationException
我知道在session.flush()
之后添加一点bars.clear();
,可以解决这个问题,但在这种情况下,我无法以非丑陋的方式访问会话。
那么冲洗是唯一的解决方案吗?还是有一个尊重行动顺序的Hibernate版本?
更新 顺便说一下,取消引用该集合将导致来自https://www.hibernate.org/117.html#A3的HibernateException:
我得到HibernateException:不要 取消引用一个集合 cascade =“all-delete-orphan”这个 如果你加载一个对象将会发生 cascade =“all-delete-orphan” 收集然后删除 参考集合。别 替换此集合,使用clear() 所以孤儿删除算法可以 检测你的变化。
答案 0 :(得分:8)
我想除了冲洗之外别无选择
来自here:
Hibernate违反了一个独特的约束条件!
Hibernate并不如此 聪明,有独特的限制因素 是外键。有时你 可能需要给一点提示。
唯一的约束违规可以 如果两个对象都存在则会发生 更新,一个是“释放”一个值 而另一个是“获得”相同的 值。解决方法是 flush() 更新后手动进行会话 第一个对象和更新之前 第二。
(这种问题很少发生在 做法。)
答案 1 :(得分:1)
如果您想避免在此处刷新会话,请尝试替换整个列表(new List<Bar>()
而不是Clear()
)。在添加新内容之前,Hibernate实际上应该删除一次中的所有项目。试一试,不确定它是否有效。
答案 2 :(得分:1)
如果您正在使用oracle,您还可以使用可延迟约束来推迟检查约束,直到提交事务为止。不确定其他数据库是否支持这种情况。
答案 3 :(得分:0)
我有一个稍微相似的问题,在某些情况下,我在子表上有一个唯一索引。所以我的问题是,当我删除一个满足此条件的记录,然后添加另一个可以恢复该条件的记录时,出现了UNIQUE INDEX问题。
经过许多试验和建议。我不喜欢在删除之后和添加之前进行刷新的想法,尽管我认为它会起作用,但是我去了一个约束解决方案并删除了唯一索引(因为我实际上并不需要对列进行索引),工作:
(在postgres数据库上)
ALTER TABLE bar ADD CONSTRAINT bar_uc
EXCLUDE (parent_id WITH =) WHERE (condition = true) INITIALLY DEFERRED;
初始化延迟是这里的关键。
我正在使用的示例代码:
//Transaction 1
Foo parent = new Foo('some-id');
boolean condition = true;
Bar c = new Bar('some-id', parent, condition);
parent.getChildren().add(c);
fooService.save(parent); //children list are cascaded
...稍后……
//Transaction 2
boolean condition = true;
Foo parent = fooSerice.findById('some-id'); //children list is eagerly fetched
Bar c1 = parent.getChildren().stream().filter(c -> c.getId().equals("some-id")).findFirst().get();
Bar c2 = new Bar('some-id1', parent, condition);
parent.getChildren().remove(c1);
parent.getChildren().add(c2);