返回介绍

关联的生命周期

发布于 2025-04-21 21:42:12 字数 3645 浏览 0 评论 0 收藏 0

Hibernate 完全负责 ALBUM_TRACKS 表的管理,当为 Album bean 的 tracks 属性增加或删除数据项时,Hibernate 会自动向 ALBUM_TRACKS 表中增加或删除相应的记录行(必要时会重新计算 LIST_POS 值)。可以写个测试程序进行测试,从我们的测试专辑中删除第 2 个曲目,然后看看结果如何。一种快速而简单的做法就是将下列 4 行代码(如例 5-12 所示)添加到例 5-8 中已有的 tx.commit()那一行的后面,然后执行 ant schema ctest attest db 命令。

例 5-12:删除专辑的第 2 个曲目

tx=session.beginTransaction();

album.getTracks().remove(1);

session.update(album);

tx.commit();

这样做会改变 ALBUM_TRACKS 表中的内容,如图 5-4 所示(和图 5-3 的原始内容相比较)。第 2 条记录已经被删除(记住,Java 列表元素的索引值是从 0 开始的),而 LIST_POS 也做了调整以保证其连续性,以便与列表元素的索引可以相对应(调用 tracks.get()所用的参数值)。

图 5-4 删除专辑的第 2 个曲目后的专辑曲目关联

之所以这样,是因为 Hibernate 明白这个列表是由 Album 表的记录“拥有”的,而这两个对象的“生命周期”(lifecycle)彼此紧密相连。设想一下,如果整个 Album 表的内容都被删除会发生什么事:ALBUM_TRACKS 里所有相关联的记录也会跟着被删除(如果你不信,可以修改测试程序来试试看)。这么一想,生命周期的概念就变得更为清晰了。</p>

ALBUM 表和 TRACK 表之间的关系就不同了。曲目有时会和专辑关联,但有时也是独立的。从列表中删除一个曲目,就会导致清除 ALBUM_TRACKS 表中相应的一行记录,专辑和曲目之间的关联也随之消失,但并不会删除 TRACK 表中的记录行,所以这么做也不会删除持久化 Track 对象本身。同样地,删除 Album 对象会把集合中所有的关联都清除,但所有实际的 Track 对象都不受影响。我们的代码只是负责在适当的时机进行这些操作(或许在向用户咨询以后,说不定有些曲目记录可以在多个专辑中共享,如前所述)。

如果我们不需要在专辑之间共享相同曲目的灵活性(对于压缩的音频文件的体积来说,其占用的磁盘空间最近相当便宜),也可以让 Hibernate 以管理 ALBUM_TRACKS 集合的相同方式来管理专辑的 TRACK 记录。Hibernate 当然不会以为它应该这么做,因为 Track 和 Album 对象能彼此独立存在,但是我们可以在专辑映射文档中为二者之间建立生命周期关系。

注意:现在,已经知道有可以自动化处理这些的方式,可能不会再感到吃惊了吧。

应该怎么做

例 5-13 展示了我们对 Album.hbm.xml 里的 tracks 属性映射做出的修改(以粗体表示)。

例 5-13:为专辑和它的曲目建立生命周期关系

<list name="tracks"table="ALBUM_TRACKS"cascade="all">

<meta attribute="use-in-tostring">true</meta>

<key column="ALBUM_ID"/>

<index column="LIST_POS"/>

<composite-element class="com.oreilly.hh.AlbumTrack">

<many-to-one class="com.oreilly.hh.Track"name="track"

cascade="all">

<meta attribute="use-in-tostring">true</meta>

<column name="TRACK_ID"/>

</many-to-one>

<property name="disc"type="integer"/>

<property name="positionOnDisc"type="integer"/>

</composite-element>

</list>

cascade 属性用于告诉 Hibernate,需要将“父”(parent)对象上施加的操作也应用到它的“子”(child)或“依赖”(dependent)对象上。这一属性适用于所有形式的集合和关联。它有几种可供选择的预设值,最常见的是 none(默认值)、save-update、delete 以及 all(组合了 save-update 和 delete)。还可以通过在 hibernate-mapping 标签内部提供一个 default-cascade 属性,将整个映射文档范围内的默认值从 none 变为 save-update。

就此例而言,我们希望专辑所包含的曲目都由专辑自动管理,这样,当删除某个专辑时,它的曲目也会随之删除。注意,我们必须为 tracks 集合和组成它的 track 元素都应用 cascade 属性,才能做到这一点。此外,将 cascade 属性设置为 all 时,就不用再明确保存我们为专辑而创建的任何 Track 对象,也就是例 5-8 中的 addAlbumTrack()方法不再需要以下这行:

session.save(track);

通过告诉 Hibernate 让它负责维护专辑及其曲目之间的关联关系,就可以让 Hibernate 在曲目被加进专辑时自动将曲目对象(Track 对象)持久保存,同时在删除专辑时也可以删除相关的所有曲目。

Hibernate 对生命周期关系的管理并不是十分安全的(fool-proof),或许更准确地说,它并非是无所不包的。例如,如果使用 Collections 接口的方法从 Album 的 tracks 属性中删除一个 Track 对象,这样会打断 Album 和 Track 之间的关联,但实际上并不会删除那个 Track 对象对应的记录。即使以后把整个 Album 删除掉,那个 Track 还会保留着,因为删除 Album 的时候,原来被删除的那个 Track 已经没有和被删除的 Album 之间有关联了。适当修改 AlbumTest.java 就能做一些这类实验,看看数据表中最后的结果!

事实上,在某些特定的情况下,Hibernate 可以负责处理这种级别的细节。只要在父子关系中使用多对一(many-to-one)映射,就可以将映射的 cascade 属性标识为 delete-orphan。相关的更多细节可以查阅 Hibernate 在线参考手册的“传播性持久化”(Transitive persistence( [1] ))一节。

将这种信息登记处理委托给映射层实在方便,这样你就能把精力集中在更为抽象和重要的任务上,所以在时机适当时值得使用。这会让人想起 Java 那令人信服的垃圾收集机制所带来的程序员的解放,但是也有一定的局限性,例如,无法通过可达性分析(reachability analysis)而明确地知道什么时候已经完成了数据的持久化。你得调用 delete(),并建立生命周期连接,才可以在代码中指明这一点。灵活性和自动化的简单性之间的得失是由你决定的,取决于数据的本质和项目的需要。

[1] http://www.hibernate.org/hib_docs/v3/reference/en/html/objectstate.html#objectstate-transitive.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。