我有两个对象,它们形成了具有多对多关系的父子关系。按照 Hibernate 参考手册中的建议,我使用连接表进行了映射:
<class name="Conference" table="conferences">
...
<set name="speakers" table="conference_speakers" cascade="all">
<key column="conference_id"/>
<many-to-many class="Speaker" column="speaker_id"/>
</set>
</class>
<class name="Speaker" table="speakers">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="firstName"/>
<property name="lastName"/>
</class>
我的愿望是单个演讲者可以与许多不同的会议相关联,而且任何会议不再引用的任何演讲者都从speakers
表中删除(作为没有关联会议的演讲者在我的项目中没有太大意义)。
但是,我发现如果我使用,那么如果仅从其中一个cascade="all-delete-orphan"
会议中删除与多个会议关联的发言人,Hibernate 会尝试删除发言人实例本身。
以下是显示此行为的单元测试:
@Test
public void testRemoveSharedSpeaker() {
int initialCount = countRowsInTable("speakers");
Conference c1 = new Conference("c1");
Conference c2 = new Conference("c2");
Speaker s = new Speaker("John", "Doe");
c1.getSpeakers().add(s);
c2.getSpeakers().add(s);
conferenceDao.saveOrUpdate(c1);
conferenceDao.saveOrUpdate(c2);
flushHibernate();
assertEquals(initialCount + 1, countRowsInTable("speakers"));
assertEquals(2, countRowsInTable("conference_speakers"));
// the remove:
c1 = conferenceDao.get(c1.getId());
c1.getSpeakers().remove(s);
flushHibernate();
assertEquals("count should stay the same", initialCount + 1, countRowsInTable("speakers"));
assertEquals(1, countRowsInTable("conference_speakers"));
c1 = conferenceDao.get(c1.getId());
c2 = conferenceDao.get(c2.getId());
assertEquals(0, c1.getSpeakers().size());
assertEquals(1, c2.getSpeakers().size());
}
s
处理' 的删除时会引发错误c1.speakers
,因为 Hibernate 正在删除连接表中的行和speakers
表中的行:
DEBUG org.hibernate.SQL - 从conference_speakers 中删除conference_id=?和speaker_id=?
调试 org.hibernate.SQL - 从 id=? 的扬声器中删除
如果我更改cascade="all-delete-orphan"
为 just cascade="all"
,则此测试按预期工作,尽管它会导致不良行为,最终我将在speakers
表中出现孤立行。
这让我想知道 - Hibernate 是否甚至有可能知道何时从关系的孩子端删除孤立对象,但只有当孩子没有被任何其他父母引用时(无论这些父母是否在当前Session
)? 也许我在滥用cascade="all-delete-orphan"
?
如果我使用 JPA 注释而不是 XML 映射,我会得到相同的行为,例如:
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "conference_speakers",
joinColumns = @JoinColumn(name = "conference_id"),
inverseJoinColumns = @JoinColumn(name = "speaker_id"))
@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Speaker> speakers = new HashSet<Speaker>();
顺便说一下,这是使用 Hibernate 3.6.7.Final 的。