📄 example-parentchild.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>第 16 章 示例:父子关系(Parent Child Relationships)</title><link rel="stylesheet" href="../shared/css/html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="up" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="previous" href="toolsetguide.html" title="第 15 章 工具箱指南"><link rel="next" href="example-weblog.html" title="第 17 章 示例:Weblog 应用程序"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">第 16 章 示例:父子关系(Parent Child Relationships)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="toolsetguide.html">上一页</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="example-weblog.html">下一页</a></td></tr></table><hr></div><div class="chapter" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title"><a name="example-parentchild"></a>第 16 章 示例:父子关系(Parent Child Relationships)</h2></div></div><div></div></div><p> 刚刚接触Hibernate的人大多是从父子关系(parent / child type relationship)的建模入手的。父子关系的建模有两种方法。比较简便、直观的方法就是在实体类<tt class="literal">Parent</tt>和<tt class="literal">Child</tt>之间建立<tt class="literal"><one-to-many></tt>的关联关系,从<tt class="literal">Parent</tt>指向<tt class="literal">Child</tt>,对新手来说尤其如此。但还有另一种方法,就是将<tt class="literal">Child</tt>声明为一个<tt class="literal"><composite-element></tt>(组合元素)。 可以看出在Hibernate中使用一对多关联比composite element更接近于通常parent / child关系的语义。下面我们会阐述如何使用<span class="emphasis"><em>双向可级联的一对多关联(bidirectional one to many association with cascades)</em></span>去建立有效、优美的parent / child关系。这一点也不难! </p><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="example-parentchild-collections"></a>16.1. 关于collections</h2></div></div><div></div></div><p> 在Hibernate下,实体类将collection作为自己的一个逻辑单元,而不是被容纳的多个实体。这非常重要!它主要体现为以下几点: </p><div class="itemizedlist"><ul type="disc"><li><p> 当删除或增加collection中对象的时候,拥有这个collection的实体对象的版本值会递增。 </p></li><li><p> 如果一个从collection中移除的对象是一个值类型(value type)的实例,比如composite element,那么这个对象的持久化状态将会终止,其在数据库中对应的记录会被删除。同样的,向collection增加一个value type的实例将会使之立即被持久化。 </p></li><li><p> 另一方面,如果从一对多或多对多关联的collection中移除一个实体,在缺省情况下这个对象并不会被删除。这个行为是完全合乎逻辑的--改变一个实体的内部状态不应该使与它关联的实体消失掉!同样的,向collection增加一个实体不会使之被持久化。 </p></li></ul></div><p> 实际上,向Collection增加一个实体的缺省动作只是在两个实体之间创建一个连接而已,同样移除的时候也只是删除连接。这种处理对于所有的情况都是合适的。不适合所有情况的其实是父子关系本身,因为子对象是否存在依赖于父对象的生存周期。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="example-parentchild-bidir"></a>16.2. 双向的一对多关系(Bidirectional one-to-many)</h2></div></div><div></div></div><p> 让我们从一个简单的例子开始,假设要实现一个从类<tt class="literal">Parent</tt>到类<tt class="literal">Child</tt>的一对多关系。 </p><pre class="programlisting"><set name="children"> <key column="parent_id"/> <one-to-many class="Child"/></set></pre><p> 如果我们运行下面的代码 </p><pre class="programlisting">Parent p = .....;Child c = new Child();p.getChildren().add(c);session.save(c);session.flush();</pre><p> Hibernate就会产生下面的两条SQL语句: </p><div class="itemizedlist"><ul type="disc"><li><p>一条<tt class="literal">INSERT</tt>语句,用于创建对象<tt class="literal">c</tt>对应的数据库记录</p></li><li><p> 一条<tt class="literal">UPDATE</tt>语句,用于创建从对象<tt class="literal">p</tt>到对象<tt class="literal">c</tt>的连接 </p></li></ul></div><p> 这样做不仅效率低,而且违反了列<tt class="literal">parent_id</tt>非空的限制。 </p><p> 底层的原因是,对象<tt class="literal">p</tt>到对象<tt class="literal">c</tt>的连接(外键<tt class="literal">parent_id</tt>)没有被当作是<tt class="literal">Child</tt>对象状态的一部分,也没有在<tt class="literal">INSERT</tt>的时候被创建。解决的办法是,在<tt class="literal">Child</tt>一端设置映射。 </p><pre class="programlisting"><many-to-one name="parent" column="parent_id" not-null="true"/></pre><p> (我们还需要为类<tt class="literal">Child</tt>添加<tt class="literal">parent</tt>属性) </p><p> 现在实体<tt class="literal">Child</tt>在管理连接的状态,为了使collection不更新连接,我们使用<tt class="literal">inverse</tt>属性。 </p><pre class="programlisting"><set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/></set></pre><p> 下面的代码是用来添加一个新的<tt class="literal">Child</tt> </p><pre class="programlisting">Parent p = (Parent) session.load(Parent.class, pid);Child c = new Child();c.setParent(p);p.getChildren().add(c);session.save(c);session.flush();</pre><p> 现在,只会有一条<tt class="literal">INSERT</tt>语句被执行! </p><p> 为了让事情变得井井有条,可以为<tt class="literal">Parent</tt>加一个<tt class="literal">addChild()</tt>方法。 </p><pre class="programlisting">public void addChild(Child c) { c.setParent(this); children.add(c);}</pre><p> 现在,添加<tt class="literal">Child</tt>的代码就是这样 </p><pre class="programlisting">Parent p = (Parent) session.load(Parent.class, pid);Child c = new Child();p.addChild(c);session.save(c);session.flush();</pre></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="example-parentchild-cascades"></a>16.3. 级联生命周期(Cascading lifecycle)</h2></div></div><div></div></div><p> 对每个对象调用<tt class="literal">save()</tt>方法很麻烦,我们可以用级联来解决这个问题。 </p><pre class="programlisting"><set name="children" inverse="true" cascade="all"> <key column="parent_id"/> <one-to-many class="Child"/></set></pre><p> 配置级联以后,代码就可以这样写: </p><pre class="programlisting">Parent p = (Parent) session.load(Parent.class, pid);Child c = new Child();p.addChild(c);session.flush();</pre><p> 同样的,保存或删除<tt class="literal">Parent</tt>对象的时候并不需要遍历其子对象。 下面的代码会删除对象<tt class="literal">p</tt>及其所有子对象对应的数据库记录。 </p><pre class="programlisting">Parent p = (Parent) session.load(Parent.class, pid);session.delete(p);session.flush();</pre><p> 然而,这段代码 </p><pre class="programlisting">Parent p = (Parent) session.load(Parent.class, pid);Child c = (Child) p.getChildren().iterator().next();p.getChildren().remove(c);c.setParent(null);session.flush();</pre><p> 不会从数据库删除<tt class="literal">c</tt>;它只会删除与<tt class="literal">p</tt>之间的连接(并且会导致违反<tt class="literal">NOT NULL</tt>约束,在这个例子中)。你需要明确调用<tt class="literal">Child</tt>的<tt class="literal">delete()</tt>方法。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -