【问题标题】:Hibernate - clearing a collection with all-delete-orphan and then adding to it causes ConstraintViolationExceptionHibernate - 使用 all-delete-orphan 清除集合然后添加到它会导致 ConstraintViolationException
【发布时间】:2011-01-05 03:13:23
【问题描述】:

我有这些实体

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 不会按照调用顺序执行操作。它不会在add()(插入)之前执行clear()(删除),反之亦然,它首先尝试插入,得到ConstraintViolationException

我知道在 bars.clear(); 之后添加一点 session.flush() 可以解决此问题,但在这种情况下,我无法以不丑陋的方式访问会话。

那么冲洗是唯一的解决方案吗?还是有尊重动作顺序的 Hibernate 版本?

更新: 顺便说一句,取消引用集合将导致来自https://www.hibernate.org/117.html#A3 的 HibernateException:

我得到 HibernateException:不要 取消引用集合 cascade="all-delete-orphan" 这个 如果你加载一个对象会发生 一个级联=“所有删除孤儿” 收集,然后删除 参考收藏。别 替换这个集合,使用 clear() 所以孤儿删除算法可以 检测你的变化。

【问题讨论】:

标签: hibernate unique-constraint


【解决方案1】:

如果您想避免在此处刷新会话,请尝试替换整个列表(new List&lt;Bar&gt;() 而不是 Clear())。 Hibernate 实际上应该在添加新项目之前一次性删除所有项目。试试看,不知道行不行。

【讨论】:

    【解决方案2】:

    我想除了冲洗别无选择

    来自here

    Hibernate 违反了唯一约束!

    Hibernate 并不像 聪明的具有独特的约束,因为它 带有外键。有时你 可能需要给一点提示。

    唯一的约束违规可能 如果两个对象都在 更新,一个是“释放”一个值 另一个是“获得”相同的 价值。一种解决方法是 flush() 更新后手动会话 第一个对象和更新之前 第二。

    (这种问题很少发生在 实践。)

    【讨论】:

      【解决方案3】:

      如果您使用的是 oracle,您还可以使用可延迟约束来推迟对约束的检查,直到事务提交。不确定其他数据库是否/如何支持。

      【讨论】:

        【解决方案4】:

        我遇到了一个稍微类似的问题,在特定条件下,我在子表上有一个唯一索引。所以我的问题是,当我删除满足此条件的记录,然后添加另一条将恢复该条件的记录时,我遇到了 UNIQUE INDEX 问题。

        经过多次试验和建议。我不喜欢在删除之后和添加之前刷新的想法,但我认为它会起作用,我去了一个约束解决方案并删除了唯一索引(因为我并不真的需要索引列)工作:

        (在 postgres 数据库上)

        ALTER TABLE bar ADD CONSTRAINT bar_uc
                EXCLUDE (parent_id WITH =) WHERE (condition = true) INITIALLY DEFERRED;
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-11
          • 1970-01-01
          • 2021-09-20
          • 2015-10-22
          • 1970-01-01
          • 2013-09-25
          • 2011-08-01
          相关资源
          最近更新 更多