10.11. 传播性持久化(transitive persistence)

优质
小牛编辑
130浏览
2023-12-01

对每一个对象都要执行保存,删除或重关联操作让人感觉有点麻烦,尤其是在处理许多彼此关联的对象的时候。一个常见的例子是父子关系。考虑下面的例子:

如 果一个父子关系中的子对象是值类型(value typed)(例如,地址或字符串的集合)的,他们的生命周期会依赖于父对象,可以享受方便的级联操作(Cascading),不需要额外的动作。父对象 被保存时,这些值类型(value typed)子对象也将被保存;父对象被删除时,子对象也将被删除。这对将一个子对象从集合中移除是同样有效:Hibernate 会检测到,并且因为值类型(value typed)的对象不可能被其他对象引用,所以 Hibernate 会在数据库中删除这个子对象。

现 在考虑同样的场景,不过父子对象都是实体(entities)类型,而非值类型(value typed)(例如,类别与个体,或母猫和小猫)。实体有自己的生命期,允许共享对其的引用(因此从集合中移除一个实体,不意味着它可以被删除),并且实 体到其他关联实体之间默认没有级联操作的设置。 Hibernate 默认不实现所谓的可到达即持久化(persistence by reachability)的策略。

每个 Hibernate session 的基本操作 — 包括 persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate() — 都有对应的级联风格(cascade style)。这些级联风格(cascade style)风格分别命名为 create, merge, save-update, delete, lock, refresh, evict, replicate。如果你希望一个操作被顺着关联关系级联传播,你必须在映射文件中指出这一点。例如:


<one-to-one name="person" cascade="persist"/>

级联风格(cascade style)是可组合的:


<one-to-one name="person" cascade="persist,delete,lock"/>

你可以使用 cascade="all" 来指定全部操作都顺着关联关系级联(cascaded)。默认值是 cascade="none",即任何操作都不会被级联(cascaded)。

注意有一个特殊的级联风格(cascade style) delete-orphan,只应用于 one-to-many 关联,表明 delete() 操作应该被应用于所有从关联中删除的对象。

建议:

  • 通常在 <many-to-one><many-to-many> 关系中应用级联(cascade)没什么意义。级联(cascade)通常在 <one-to-one><one-to-many> 关系中比较有用。

  • 如果子对象的寿命限定在父亲对象的寿命之内,可通过指定 cascade="all,delete-orphan" 将其变为自动生命周期管理的对象(lifecycle object)

  • 其他情况,你可根本不需要级联(cascade)。但是如果你认为你会经常在某个事务中同时用到父对象与子对象,并且你希望少打点儿字,可以考虑使用 cascade="persist,merge,save-update"

可以使用 cascade="all" 将一个关联关系(无论是对值对象的关联,或者对一个集合的关联)标记为父/子关系的关联。 这样对父对象进行 save/update/delete 操作就会导致子对象也进行 save/update/delete 操作。

此外,一个持久的父对象对子对象的浅引用(mere reference)会导致子对象被同步 save/update。不过,这个隐喻(metaphor)的说法并不完整。除非关联是 <one-to-many> 关联并且被标记为 cascade="delete-orphan",否则父对象失去对某个子对象的引用不会导致该子对象被自动删除。父子关系的级联(cascading)操作准确语义如下:

  • 如果父对象被 persist(),那么所有子对象也会被 persist()

  • 如果父对象被 merge(),那么所有子对象也会被 merge()

  • 如果父对象被 save()update()saveOrUpdate(),那么所有子对象则会被 saveOrUpdate()

  • 如果某个持久的父对象引用了瞬时(transient)或者脱管(detached)的子对象,那么子对象将会被 saveOrUpdate()

  • 如果父对象被删除,那么所有子对象也会被 delete()

  • 除非被标记为 cascade="delete-orphan"(删除“孤儿”模式,此时不被任何一个父对象引用的子对象会被删除),否则子对象失掉父对象对其的引用时,什么事也不会发生。如果有特殊需要,应用程序可通过显式调用 delete() 删除子对象。

最后,注意操作的级联可能是在调用期(call time)或者写入期(flush time)作用到对象图上的。所有的操作,如果允许,都在操作被执行的时候级联到可触及的关联实体上。然而,save-upatedelete-orphan 是在Session flush 的时候才作用到所有可触及的被关联对象上的。