3

我有两个实体 Person 和 Preference。两者之间存在多对多的关系。人和偏好关系存储在映射表中

CREATE TABLE `person_2_preference` (
`person_id` varchar(32) NOT NULL,
`preference_id` varchar(32) NOT NULL,
`sort_order` int(11) NOT NULL,
 PRIMARY KEY (`person_id`,`preference_id`);

一个人有一个有序的偏好列表,如下所示。

<list  name="preferences" table="person_2_preference" lazy="true" inverse="false" 
        cascade="save-update">
            <key column="person_id" update="false"/>
            <index column="sort_order"/>
            <many-to-many class="com.xx.Preference" column="preference_id" />
        </list>

在 java 类中,我想在某些情况下重新排序首选项,所以我正在执行 Collections.swap 之类的 Collections.swap(person.getPreferences(), index, index++) (我有验证以确保索引不是超出范围)

当我保存人 - personDao.saveOrUpdate(person) 时,由于主键,我得到一个唯一的约束违规异常。

我还有其他一对多的有序关系,使用 Collections.swap 重新排序效果很好。这是对多对多关系的限制吗?谢谢你的时间

4

1 回答 1

1

我认为这个问题来自 Hibernate 的内部存储结构,它被你的交换弄糊涂了。Hibernate 还将列表的对象存储在一级缓存中,并且它在某处存储了实际在数据库中的值,因此它可以决定是否需要更新。

当你改变列表中的顺序时,有两种更新方式:要么让person_id和preference_id不变,修改sort_order,要么让sort_order不变,修改person_id和preference_id。在交换的情况下,第二种方式导致在两次更新中的第一次之后违反唯一约束。由于某些与 Hibernate 内部组织有关的原因,Hibernate 以第二种方式进行更新。

如何解决这个问题有(至少)三种不同的可能性:

  1. 您将序列添加到表 person_2_preference 并将此序列作为主键(Hibernate 喜欢只有一列的主键)。我没有对此进行测试,我不能 100% 确定它是否能解决您的问题。

  2. 您可以通过 HQL 更新语句手动进行更新:

    UPDATE Person2Preference SET sortOrder= :order WHERE personId=:person and preferenceId=:preference

    Person2Perference 是映射到 person_2_preference 的类。这必须为每个子对象完成。在此之后evict(),可能会推荐使用父对象和/或子对象(Hibernate 在此更新后不会更新第一级缓存)。

  3. 您不会交换从 Hibernate 获得的列表中的元素。您将此列表复制到数组列表(或数组)中,并且仅在副本中进行交换。这不需要太多内存,因为在您的副本中只有对对象的引用。在 Hibernate 对象中,您只更新排序顺序。

我可能会做解决方案 3。解决方案 1 如果对新序列有任何其他需要,我会做。

于 2013-01-10T08:21:08.277 回答