13

我有这些实体

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()在 (insert) 之前执行 (delete),add()反之亦然,它首先尝试插入,得到一个ConstraintViolationException

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

那么flush是唯一的解决方案吗?还是有尊重动作顺序的休眠版本?

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

我得到 HibernateException: Don't dereference a collection with cascade="all-delete-orphan"如果您使用 cascade="all-delete-orphan" 集合加载对象然后删除对集合的引用,则会发生这种情况。不要替换此集合,请使用 clear() 以便孤儿删除算法可以检测到您的更改。

4

4 回答 4

8

我想除了冲洗别无选择

这里

Hibernate 违反了一个独特的约束!

Hibernate 在使用唯一约束时并不像使用外键那样聪明。有时您可能需要提供一些提示。

如果两个对象都被更新,一个是“释放”一个值,另一个是“获取”相同的值,则可能会发生唯一约束冲突。一种解决方法是在更新第一个对象之后和更新第二个对象之前手动 刷新()会话。

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

于 2010-01-14T16:45:26.487 回答
1

如果您想避免在此处刷新会话,请尝试替换整个列表(new List<Bar>()而不是Clear())。Hibernate 实际上应该在添加新项目之前一次性删除所有项目。只是尝试,不确定它是否有效。

于 2010-01-14T16:19:59.843 回答
1

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

于 2010-01-20T06:21:00.923 回答
0

我有一个稍微类似的问题,在某些条件下我在子表上有一个唯一索引。所以我的问题是,当我删除一条满足此条件的记录,然后添加另一条将恢复该条件的记录时,我遇到了 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);
于 2020-07-25T02:25:58.723 回答