- 译者序
- 前言
- 本书怎么使用
- 本书排版字体约定
- 本书网站
- 致谢
- 第一部分 Hibernate 快速入门
- 第 1 章 安装和设置
- 第 2 章 映射简介
- 第 3 章 驾驭 Hibernate
- 第 4 章 集合与关联
- 第 5 章 更复杂的关联
- 第 6 章 自定义值类型
- 第 7 章 映射标注
- 第 8 章 条件查询
- 第 9 章 浅谈 HQL
- 第二部分 与其他工具的集成
- 第 10 章 将 Hibernate 连接到 MySQL
- 第 11 章 Hibernate 与 Eclipse:Hibernate Tools 使用实战
- 第 12 章 Maven 进阶
- 第 13 章 Spring 入门:Hibernate 与 Spring
- 第 14 章 画龙点睛:用 Stripes 集成 Spring 和 Hibernate
- 附录 A Hibernate 类型
- 附录 B Criteria API
- 附录 C Hibernate SQL 方言
- 附录 D Spring 事务支持
- 附录 E 参考资源
- 作者简介
- 封面介绍
关联的生命周期
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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论