📄 example-parentchild.html
字号:
</p><pre class="programlisting">Parent p = (Parent) session.load(Parent.class, pid);Child c = (Child) p.getChildren().iterator().next();p.getChildren().remove(c);session.delete(c);session.flush();</pre><p> 在我们的例子中,如果我们规定没有父对象的话,子对象就不应该存在,如果将子对象从collection中移除,实际上我们是想删除它。要实现这种要求,就必须使用<tt class="literal">cascade="all-delete-orphan"</tt>。 </p><pre class="programlisting"><set name="children" inverse="true" cascade="all-delete-orphan"> <key column="parent_id"/> <one-to-many class="Child"/></set></pre><p> 注意:即使在collection一方的映射中指定<tt class="literal">inverse="true"</tt>,在遍历collection的时候级联操作仍然会执行。如果你想要通过级联进行子对象的插入、删除、更新操作,就必须把它加到collection中,只调用<tt class="literal">setParent()</tt>是不够的。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="example-parentchild-update"></a>16.4. 级联更新(Using cascading <tt class="literal">update()</tt>)</h2></div></div><div></div></div><p> 假设我们从<tt class="literal">Session</tt>中装入了一个<tt class="literal">Parent</tt>对象,用户界面对其进行了修改,然后我们希望在一个新的Session里面调用<tt class="literal">update()</tt>来更新它。对象<tt class="literal">Parent</tt>包含了子对象的集合,由于打开了级联更新,Hibernate需要知道哪些子对象是新的,哪些是数据库中已经存在的。我们假设<tt class="literal">Parent</tt>和<tt class="literal">Child</tt>对象的标识属性的类型为<tt class="literal">java.lang.Long</tt>。Hibernate会使用标识属性的值来判断哪些子对象是新的。(你也可以使用version 或 timestamp 属性,参见<a href="manipulatingdata.html#manipulatingdata-updating-detached" title="9.4.2. 更新从session脱离的对象">第 9.4.2 节 “更新从session脱离的对象”</a>.) </p><p> <tt class="literal">unsaved-value</tt>属性是用来表示新实例的标识属性值的,缺省为"null",用在<tt class="literal">Long</tt>类型的标识类型再好不过了。如果我们使用原始类型作为标识类型的话,我们在配置<tt class="literal">Child</tt>类映射的时候就必须写: </p><pre class="programlisting"><id name="id" type="long" unsaved-value="0"></pre><p> (为版本和时间戳属性进行映射,也会有另一个叫做<tt class="literal">unsaved-value</tt>的属性。) </p><p> 下面的代码会更新<tt class="literal">parent</tt>和<tt class="literal">child</tt>对象,并且插入<tt class="literal">newChild</tt>对象。 </p><pre class="programlisting">//parent and child were both loaded in a previous sessionparent.addChild(child);Child newChild = new Child();parent.addChild(newChild);session.update(parent);session.flush();</pre><p> 好的,对于自动生成标识的情况这样做很方便,但是自分配的标识和复合标识怎么办呢?这是有点麻烦,因为unsaved-value无法区分新对象(标识是用户指定的)和前一个Session装入的对象。在这种情况下,你可能需要给Hibernate一些提示,在调用<tt class="literal">update(parent)</tt>之前: </p><div class="itemizedlist"><ul type="disc"><li><p> 在这个类的<tt class="literal"><version></tt> or <tt class="literal"><timestamp></tt>属性映射上定义<tt class="literal">unsaved-value="null"</tt>或者<tt class="literal">unsaved-value="negative"</tt>。 </p></li><li><p> 在对父对象执行<tt class="literal">update(parent)</tt>之前,设定<tt class="literal">unsaved-value="none"</tt>并且显式的调用<tt class="literal">save()</tt>在数据库创建新子对象 </p></li><li><p> 在对父对象执行<tt class="literal">update(parent)</tt>之前,设定<tt class="literal">unsaved-value="any"</tt>并且显式的调用<tt class="literal">update()</tt>更新已经装入的子对象 </p></li></ul></div><p> <tt class="literal">none</tt>是自分配标识和复合标识的<tt class="literal">unsaved-value</tt>的缺省值。 </p><p> 还有一种可能情况,有一个名为<tt class="literal">isUnsaved()</tt>的<tt class="literal">拦截器(Interceptor)</tt>方法,它允许应用程序自己实现新实例的判断。比如,你可以自己定义一个持久类的祖先类: </p><pre class="programlisting">public class Persistent { private boolean _saved = false; public void onSave() { _saved=true; } public void onLoad() { _saved=true; } ...... public boolean isSaved() { return _saved; }}</pre><p> (<tt class="literal">saved</tt>属性是不会被持久化的。) 现在在<tt class="literal">onLoad()</tt>和<tt class="literal">onSave()</tt>外,还要实现<tt class="literal">isUnsaved()</tt>。 </p><pre class="programlisting">public Boolean isUnsaved(Object entity) { if (entity instanceof Persistent) { return new Boolean( !( (Persistent) entity ).isSaved() ); } else { return null; }}public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof Persistent) ( (Persistent) entity ).onLoad(); return false;}public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof Persistent) ( (Persistent) entity ).onSave(); return false;}</pre></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="example-parentchild-conclusion"></a>16.5. 结论</h2></div></div><div></div></div><p> 这个问题往往让新手感到迷惑,它确实不太容易消化。不过,经过一些实践以后,你会感觉越来越顺手。父子对象模式已经被广泛的应用在Hibernate应用程序中。 </p><p> 在第一段中我们曾经提到另一个方案。复合元素的语义与父子关系是等同的,但是我们并没有详细讨论。很不幸复合元素还有两个重大限制:复合元素不能拥有collections,并且,除了用于惟一的父对象外,它们不能再作为其它任何实体的子对象。(但是,通过使用<tt class="literal"><idbag></tt>映射,它们<span class="emphasis"><em>可能</em></span>拥有代理主键。) </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="toolsetguide.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="index.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="example-weblog.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">第 15 章 工具箱指南 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 第 17 章 示例:Weblog 应用程序</td></tr></table></div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -