繁体   English   中英

cascade =“all-delete-orphan”在与连接表的Hibernate单向多对多关联中有什么意义吗?

[英]Does cascade=“all-delete-orphan” have any meaning in a Hibernate unidirectional many-to-many association with a join table?

我有两个对象形成父子关系,它们具有多对多的关系。 按照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>

我希望单个发言者可以与许多不同的会议相关联,而且任何会议不再引用的speakersspeakers表中删除(因为没有相关会议的发言人在我的会议中没有多大意义)项目)。

但是,我发现如果我使用cascade="all-delete-orphan" ,那么如果其中一个会话中删除与多个会议相关联的扬声器,则Hibernate会尝试删除Speaker实例本身。

下面是一个显示此行为的单元测试:

@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 =?
DEBUG org.hibernate.SQL - 从id =?的扬声器中删除

如果我将cascade="all-delete-orphan"改为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。

DELETE_ORPHAN级联模式没有为多对多关系定义 - 仅适用于一对多(后者在JPA标准@OneToMany注释中运行“orphanRemoval = true | false”属性,因此您不必诉诸于专有的Hibernate注释)。

其原因正如您所描述的那样 - Hibernate没有办法弄清楚多对多关系的“孤立”结尾是否真正孤立而不对数据库运行查询,这既反直觉又可能(可能)具有严重的性能影响。

因此,您所描述的Hibernate行为是正确的(好吧,“如文档所述”); 虽然在一个完美的世界中,它会提醒你DELETE_ORPHAN在第二次传递映射编译期间在多对多方面是非法的。

说实话,我想不出一个实现你想做的好方法。 最简单(但特定于数据库)的方式可能是定义从conference_speakers删除的触发器,该触发器将检查该扬声器是否“真正”孤立并且如果是这样将其从speakers删除。 与数据库无关的选项是在DAO或侦听器中手动执行相同操作。

更新:这里是Hibernate文档的摘录(第11.11节,紧跟在CascadeType.ALL上的灰色注释之后),重点是我的:

特殊的级联样式delete-orphan仅适用于一对多关联 ,并指示delete()操作应应用于从关联中删除的任何子对象。

再向下:

在多对一或多对多关联上启用级联通常没有意义。 实际上@ManyToOne和@ManyToMany甚至不提供orphanRemoval属性。 级联通常对一对一和一对多关联有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM