Hibernate - 使用all-delete-orphan清除集合然后添加到它会导致ConstraintViolationException

时间:2010-01-14 15:35:37

标签: hibernate unique-constraint

我有这些实体

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_identifierparent_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()   所以孤儿删除算法可以   检测你的变化。

4 个答案:

答案 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);