我有两个实体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的重新排序效果很好。这是对多对多关系的限制吗?谢谢你的时间
答案 0 :(得分:1)
我认为问题来自Hibernate的内部存储结构,它会被你的交换混淆。 Hibernate还将列表中的对象存储在第一级缓存中,并且它存储了实际存储在数据库中的值,以便它可以决定是否需要更新。
当您更改列表中的顺序时,有两种更新方式:您可以更改person_id和preference_id并修改sort_order,或者让sort_order保持不变并修改person_id和preference_id。在交换的情况下,第二种方式在两次更新中的第一次之后导致唯一的约束违规。由于某些原因,这与Hibernate的内部组织有关,Hibernate以第二种方式进行更新。
如何解决问题(至少)有三种不同的可能性:
您将一个序列添加到表person_2_preference中,并将此序列作为主键(Hibernate喜欢只有一个列的主键)。我没有对此进行测试,我不能100%确定它是否能解决您的问题。
您可以通过HQL更新语句手动执行更新:
UPDATE Person2Preference SET sortOrder= :order WHERE personId=:person and preferenceId=:preference
Person2Perference是映射到person_2_preference的类。必须为每个子对象执行此操作。在此之后,可能推荐evict()
父对象和/或子对象(Hibernate在此更新后不更新第一级缓存)。
您不会在列表中交换您从Hibernate获得的结果。您将此列表复制到数组列表(或数组),并且只在副本中执行交换。这不需要太多内存,因为在您的副本中只有对象的引用。在Hibernate对象中,您只需更新排序顺序。
我可能会做解决方案3.解决方案1如果对新系列有任何其他需要,我会这样做。