~raginggoblin/infolog/infolog

« back to all changes in this revision

Viewing changes to InfologServer/lib/hibernate-distribution-3.3.2.GA/project/documentation/manual/src/main/docbook/en-US/content/example_parentchild.xml

  • Committer: Raging Goblin
  • Date: 2013-11-16 16:51:32 UTC
  • Revision ID: raging_goblin-20131116165132-weujnptzc88uy4ah
Mavenized the project, now using shared project InfologSync

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?xml version='1.0' encoding="UTF-8"?>
2
 
<!--
3
 
  ~ Hibernate, Relational Persistence for Idiomatic Java
4
 
  ~
5
 
  ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
6
 
  ~ indicated by the @author tags or express copyright attribution
7
 
  ~ statements applied by the authors.  All third-party contributions are
8
 
  ~ distributed under license by Red Hat Middleware LLC.
9
 
  ~
10
 
  ~ This copyrighted material is made available to anyone wishing to use, modify,
11
 
  ~ copy, or redistribute it subject to the terms and conditions of the GNU
12
 
  ~ Lesser General Public License, as published by the Free Software Foundation.
13
 
  ~
14
 
  ~ This program is distributed in the hope that it will be useful,
15
 
  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16
 
  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
17
 
  ~ for more details.
18
 
  ~
19
 
  ~ You should have received a copy of the GNU Lesser General Public License
20
 
  ~ along with this distribution; if not, write to:
21
 
  ~ Free Software Foundation, Inc.
22
 
  ~ 51 Franklin Street, Fifth Floor
23
 
  ~ Boston, MA  02110-1301  USA
24
 
  -->
25
 
 
26
 
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
27
 
 
28
 
<chapter id="example-parentchild">
29
 
    <title>Example: Parent/Child</title>
30
 
 
31
 
    <para>
32
 
        One of the first things that new users want to do with Hibernate is to model a parent/child type 
33
 
        relationship. There are two different approaches to this. The most convenient 
34
 
        approach, especially for new users, is to model both <literal>Parent</literal> and <literal>Child</literal> 
35
 
        as entity classes with a <literal>&lt;one-to-many&gt;</literal> association from <literal>Parent</literal> 
36
 
        to <literal>Child</literal>. The alternative approach is to declare the <literal>Child</literal> as a 
37
 
        <literal>&lt;composite-element&gt;</literal>. The default semantics of a one-to-many 
38
 
        association in Hibernate are much less close to the usual semantics of a parent/child relationship than 
39
 
        those of a composite element mapping. We will explain how to use a <emphasis>bidirectional one-to-many 
40
 
        association with cascades</emphasis> to model a parent/child relationship efficiently and elegantly. 
41
 
        
42
 
    </para>
43
 
    
44
 
    <sect1 id="example-parentchild-collections">
45
 
        <title>A note about collections</title>
46
 
 
47
 
        <para>
48
 
            Hibernate collections are considered to be a logical part of their owning entity and not of the
49
 
            contained entities. Be aware that this is a critical distinction that has the following consequences:
50
 
        </para>
51
 
 
52
 
        <itemizedlist>
53
 
            <listitem>
54
 
            <para>
55
 
                When you remove/add an object from/to a collection, the version number of the collection owner
56
 
                is incremented.
57
 
            </para>
58
 
            </listitem>
59
 
            <listitem>
60
 
            <para>
61
 
                If an object that was removed from a collection is an instance of a value type (e.g. a composite
62
 
                element), that object will cease to be persistent and its state will be completely removed from
63
 
                the database. Likewise, adding a value type instance to the collection will cause its state to be
64
 
                immediately persistent.
65
 
            </para>
66
 
            </listitem>
67
 
            <listitem>
68
 
            <para>
69
 
                Conversely, if an entity is removed from a collection (a one-to-many or many-to-many
70
 
                association), it will not be deleted by default. This behavior is completely consistent; a
71
 
                change to the internal state of another entity should not cause the associated entity to vanish.
72
 
                Likewise, adding an entity to a collection does not cause that entity to become persistent, by
73
 
                default.
74
 
            </para>
75
 
            </listitem>
76
 
        </itemizedlist>
77
 
 
78
 
        <para>
79
 
            Adding an entity to a collection, by default, merely creates a link between
80
 
            the two entities. Removing the entity will remove the link. This is appropriate for all sorts of cases.
81
 
            However, it is not appropriate in the case of a parent/child relationship. In this case, the life of the
82
 
            child is bound to the life cycle of the parent.
83
 
        </para>
84
 
    
85
 
    </sect1>
86
 
 
87
 
    <sect1 id="example-parentchild-bidir">
88
 
        <title>Bidirectional one-to-many</title>
89
 
 
90
 
        <para>
91
 
            Suppose we start with a simple <literal>&lt;one-to-many&gt;</literal> association from
92
 
            <literal>Parent</literal> to <literal>Child</literal>.
93
 
        </para>
94
 
 
95
 
        <programlisting><![CDATA[<set name="children">
96
 
    <key column="parent_id"/>
97
 
    <one-to-many class="Child"/>
98
 
</set>]]></programlisting>
99
 
    
100
 
        <para>
101
 
            If we were to execute the following code:
102
 
        </para>
103
 
 
104
 
        <programlisting><![CDATA[Parent p = .....;
105
 
Child c = new Child();
106
 
p.getChildren().add(c);
107
 
session.save(c);
108
 
session.flush();]]></programlisting>
109
 
    
110
 
        <para>
111
 
            Hibernate would issue two SQL statements:
112
 
        </para>
113
 
 
114
 
        <itemizedlist>
115
 
        <listitem>
116
 
            <para>an <literal>INSERT</literal> to create the record for <literal>c</literal></para>
117
 
        </listitem>
118
 
        <listitem>
119
 
            <para>
120
 
                an <literal>UPDATE</literal> to create the link from <literal>p</literal> to
121
 
                <literal>c</literal>
122
 
            </para>
123
 
        </listitem>
124
 
        </itemizedlist>
125
 
    
126
 
        <para>
127
 
            This is not only inefficient, but also violates any <literal>NOT NULL</literal> constraint on the
128
 
            <literal>parent_id</literal> column. You can fix the nullability constraint violation by specifying
129
 
            <literal>not-null="true"</literal> in the collection mapping:
130
 
        </para>
131
 
 
132
 
        <programlisting><![CDATA[<set name="children">
133
 
    <key column="parent_id" not-null="true"/>
134
 
    <one-to-many class="Child"/>
135
 
</set>]]></programlisting>
136
 
    
137
 
        <para>
138
 
                However, this is not the recommended solution.
139
 
        </para>
140
 
        <para>
141
 
            The underlying cause of this behavior is that the link (the foreign key <literal>parent_id</literal>) 
142
 
            from <literal>p</literal> to <literal>c</literal> is not considered part of the state of the 
143
 
            <literal>Child</literal> object and is therefore not created in the <literal>INSERT</literal>. The 
144
 
            solution is to make the link part of the <literal>Child</literal> mapping.
145
 
        </para>
146
 
 
147
 
        <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
148
 
 
149
 
        <para>
150
 
            You also need to add the <literal>parent</literal> property to the <literal>Child</literal> class.
151
 
        </para>
152
 
 
153
 
        <para>
154
 
            Now that the <literal>Child</literal> entity is managing the state of the link, we tell the collection 
155
 
            not to update the link. We use the <literal>inverse</literal> attribute to do this:
156
 
        </para>
157
 
 
158
 
        <programlisting><![CDATA[<set name="children" inverse="true">
159
 
    <key column="parent_id"/>
160
 
    <one-to-many class="Child"/>
161
 
</set>]]></programlisting>
162
 
 
163
 
        <para>
164
 
            The following code would be used to add a new <literal>Child</literal>:
165
 
        </para>
166
 
 
167
 
        <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
168
 
Child c = new Child();
169
 
c.setParent(p);
170
 
p.getChildren().add(c);
171
 
session.save(c);
172
 
session.flush();]]></programlisting>
173
 
 
174
 
        <para>
175
 
            Only one SQL <literal>INSERT</literal> would now be issued.
176
 
        </para>
177
 
 
178
 
        <para>
179
 
            You could also create an <literal>addChild()</literal> method of
180
 
            <literal>Parent</literal>.
181
 
        </para>
182
 
 
183
 
        <programlisting><![CDATA[public void addChild(Child c) {
184
 
    c.setParent(this);
185
 
    children.add(c);
186
 
}]]></programlisting>
187
 
 
188
 
        <para>
189
 
            The code to add a <literal>Child</literal> looks like this:
190
 
        </para>
191
 
 
192
 
        <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
193
 
Child c = new Child();
194
 
p.addChild(c);
195
 
session.save(c);
196
 
session.flush();]]></programlisting>
197
 
 
198
 
     </sect1>
199
 
     
200
 
     <sect1 id="example-parentchild-cascades">
201
 
         <title>Cascading life cycle</title>
202
 
     
203
 
         <para>
204
 
             You can address the frustrations of the explicit call to <literal>save()</literal> by
205
 
             using cascades.
206
 
         </para>
207
 
 
208
 
        <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
209
 
    <key column="parent_id"/>
210
 
    <one-to-many class="Child"/>
211
 
</set>]]></programlisting>
212
 
     
213
 
         <para>
214
 
             This simplifies the code above to:
215
 
         </para>
216
 
 
217
 
        <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
218
 
Child c = new Child();
219
 
p.addChild(c);
220
 
session.flush();]]></programlisting>
221
 
     
222
 
         <para>
223
 
             Similarly, we do not need to iterate over the children when saving or deleting a <literal>Parent</literal>.
224
 
             The following removes <literal>p</literal> and all its children from the database.
225
 
         </para>
226
 
 
227
 
         <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
228
 
session.delete(p);
229
 
session.flush();]]></programlisting>
230
 
     
231
 
         <para>
232
 
             However, the following code:
233
 
         </para>
234
 
 
235
 
         <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
236
 
Child c = (Child) p.getChildren().iterator().next();
237
 
p.getChildren().remove(c);
238
 
c.setParent(null);
239
 
session.flush();]]></programlisting>
240
 
     
241
 
         <para>
242
 
             will not remove <literal>c</literal> from the database. In this case, it will only remove the link to <literal>p</literal>
243
 
             and cause a <literal>NOT NULL</literal> constraint violation. You need to explicitly
244
 
             <literal>delete()</literal> the <literal>Child</literal>.
245
 
         </para>
246
 
 
247
 
         <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
248
 
Child c = (Child) p.getChildren().iterator().next();
249
 
p.getChildren().remove(c);
250
 
session.delete(c);
251
 
session.flush();]]></programlisting>
252
 
 
253
 
         <para>
254
 
             In our case, a <literal>Child</literal> cannot exist without its parent. So if we remove
255
 
             a <literal>Child</literal> from the collection, we do want it to be deleted. To do this, we must
256
 
             use <literal>cascade="all-delete-orphan"</literal>.
257
 
         </para>
258
 
 
259
 
        <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
260
 
    <key column="parent_id"/>
261
 
    <one-to-many class="Child"/>
262
 
</set>]]></programlisting>
263
 
 
264
 
         <para>
265
 
             Even though the collection mapping specifies <literal>inverse="true"</literal>, cascades are 
266
 
             still processed by iterating the collection elements. If you need an object be saved, 
267
 
             deleted or updated by cascade, you must add it to the collection. It is not enough to simply call
268
 
             <literal>setParent()</literal>.
269
 
         </para>
270
 
               
271
 
     </sect1>
272
 
     
273
 
     <sect1 id="example-parentchild-update">
274
 
         <title>Cascades and <literal>unsaved-value</literal></title>
275
 
     
276
 
         <para>
277
 
             Suppose we loaded up a <literal>Parent</literal> in one <literal>Session</literal>, made some changes 
278
 
             in a UI action and wanted to persist these changes in a new session by calling <literal>update()</literal>. 
279
 
             The <literal>Parent</literal> will contain a collection of children and, since the cascading update is enabled, 
280
 
             Hibernate needs to know which children are newly instantiated and which represent existing rows in the 
281
 
             database. We will also assume that both <literal>Parent</literal> and <literal>Child</literal> have generated
282
 
             identifier properties of type <literal>Long</literal>. Hibernate will use the identifier and 
283
 
             version/timestamp property value to determine which of the children are new. (See
284
 
             <xref linkend="objectstate-saveorupdate"/>.) <emphasis>In Hibernate3, it is no longer necessary to specify
285
 
             an <literal>unsaved-value</literal> explicitly.</emphasis>
286
 
         </para>
287
 
 
288
 
         <para>
289
 
             The following code will update <literal>parent</literal> and <literal>child</literal> and insert 
290
 
             <literal>newChild</literal>:
291
 
         </para>
292
 
 
293
 
         <programlisting><![CDATA[//parent and child were both loaded in a previous session
294
 
parent.addChild(child);
295
 
Child newChild = new Child();
296
 
parent.addChild(newChild);
297
 
session.update(parent);
298
 
session.flush();]]></programlisting>
299
 
     
300
 
         <para>
301
 
             This may be suitable for the case of a generated identifier, but what about assigned identifiers
302
 
             and composite identifiers? This is more difficult, since Hibernate cannot use the identifier property to
303
 
             distinguish between a newly instantiated object, with an identifier assigned by the user, and an 
304
 
             object loaded in a previous session. In this case, Hibernate will either use the timestamp or version 
305
 
             property, or will actually query the second-level cache or, worst case, the database, to see if the 
306
 
             row exists.
307
 
         </para>
308
 
         
309
 
         <!-- undocumenting
310
 
         <para>
311
 
             There is one further possibility. The <literal>Interceptor</literal> method named 
312
 
             <literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
313
 
             newly instantiated objects. For example, you could define a base class for your persistent classes.
314
 
         </para>
315
 
 
316
 
         <programlisting><![CDATA[public class Persistent {
317
 
    private boolean _saved = false;
318
 
    public void onSave() {
319
 
        _saved=true;
320
 
    }
321
 
    public void onLoad() {
322
 
        _saved=true;
323
 
    }
324
 
    ......
325
 
    public boolean isSaved() {
326
 
        return _saved;
327
 
    }
328
 
}]]></programlisting>
329
 
     
330
 
         <para>
331
 
             (The <literal>saved</literal> property is non-persistent.)
332
 
             Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
333
 
             and <literal>onSave()</literal> as follows.
334
 
         </para>
335
 
 
336
 
         <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
337
 
    if (entity instanceof Persistent) {
338
 
        return new Boolean( !( (Persistent) entity ).isSaved() );
339
 
    }
340
 
    else {
341
 
        return null;
342
 
    }
343
 
}
344
 
 
345
 
public boolean onLoad(Object entity, 
346
 
    Serializable id,
347
 
    Object[] state,
348
 
    String[] propertyNames,
349
 
    Type[] types) {
350
 
 
351
 
    if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
352
 
    return false;
353
 
}
354
 
 
355
 
public boolean onSave(Object entity,
356
 
    Serializable id,
357
 
    Object[] state,
358
 
    String[] propertyNames,
359
 
    Type[] types) {
360
 
        
361
 
    if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
362
 
    return false;
363
 
}]]></programlisting>
364
 
 
365
 
                <para>
366
 
                        Do not worry; in Hibernate3 you do not need to write any of this kind of code if you do not want to.
367
 
                </para>
368
 
     -->
369
 
     </sect1>
370
 
 
371
 
     <sect1 id="example-parentchild-conclusion">
372
 
         <title>Conclusion</title>
373
 
 
374
 
         <para>
375
 
             The sections we have just covered can be a bit confusing. However, in practice, 
376
 
             it all works out nicely. Most Hibernate applications use the parent/child pattern in many places.
377
 
         </para>
378
 
 
379
 
         <para>
380
 
             We mentioned an alternative in the first paragraph. None of the above issues exist in the case of
381
 
             <literal>&lt;composite-element&gt;</literal> mappings, which have exactly the semantics of a parent/child
382
 
             relationship. Unfortunately, there are two big limitations with composite element classes: composite elements 
383
 
             cannot own collections and they should not be the child of any entity other than the unique parent.
384
 
         </para>
385
 
     
386
 
     </sect1>
387
 
     
388
 
</chapter>