diff --git a/reference/en/modules/example_parentchild.xml b/reference/en/modules/example_parentchild.xml
index e4247f85b1..09659eac6b 100644
--- a/reference/en/modules/example_parentchild.xml
+++ b/reference/en/modules/example_parentchild.xml
@@ -10,8 +10,8 @@
<composite-element>.) Now, it turns out that default semantics of a one to many
association (in Hibernate) are much less close to the usual semantics of a parent / child relationship than
those of a composite element mapping. We will explain how to use a bidirectional one to many
- association with cascades to model a parent / child relationship efficiently and elegantly. It's
- not at all difficult!
+ association with cascades to model a parent / child relationship efficiently and elegantly.
+ It's not at all difficult!
@@ -98,14 +98,23 @@ session.flush();]]>
This is not only inefficient, but also violates any NOT NULL constraint on the
- parent_id column.
+ parent_id column. We can fix the nullability constraint violation by specifying
+ not-null="true" in the collection mapping:
+
+
+
+]]>
+
- The underlying cause is that the link (the foreign key parent_id) from
- p to c is not considered part of the state of the Child
- object and is therefore not created in the INSERT. So the solution is to make the link part
- of the Child mapping.
+ However, this is not the recommended solution.
+
+
+ The underlying cause of this behaviour is that the link (the foreign key parent_id)
+ from p to c is not considered part of the state of the
+ Child object and is therefore not created in the INSERT. So the
+ solution is to make the link part of the Child mapping.
]]>
@@ -115,8 +124,8 @@ session.flush();]]>
- Now that the Child entity is managing the state of the link, we tell the collection not
- to update the link. We use the inverse attribute.
+ Now that the Child entity is managing the state of the link, we tell the collection
+ not to update the link. We use the inverse attribute.
@@ -226,35 +235,37 @@ session.flush();]]>
]]>
- Note: even though the collection mapping specifies inverse="true", cascades are still
- processed by iterating the collection elements. So if you require that an object be saved, deleted or
- updated by cascade, you must add it to the collection. It is not enough to simply call
+ Note: even though the collection mapping specifies inverse="true", cascades are
+ still processed by iterating the collection elements. So if you require that an object be saved,
+ deleted or updated by cascade, you must add it to the collection. It is not enough to simply call
setParent().
- Using cascading update()
+ Cascades and unsaved-value
- Suppose we loaded up a Parent in one Session, made some changes in a UI
- action and wish to persist these changes in a new Session (by calling update()). The
- Parent will contain a collection of childen and, since cascading update is enabled, Hibernate
- needs to know which children are newly instantiated and which represent existing rows in the database. Lets assume
- that both Parent and Child have (synthetic) identifier properties of type
- java.lang.Long. Hibernate will use the identifier property value to determine which of the
- children are new. (You may also use the version or timestamp property, see
+ Suppose we loaded up a Parent in one Session, made some changes
+ in a UI action and wish to persist these changes in a new session by calling update().
+ The Parent will contain a collection of childen and, since cascading update is enabled,
+ Hibernate needs to know which children are newly instantiated and which represent existing rows in the
+ database. Lets assume that both Parent and Child have genenerated
+ identifier properties of type Long. Hibernate will use the identifier property
+ value to determine which of the children are new. (You may also use the version or timestamp property, see
.)
- The unsaved-value attribute is used to specify the identifier value of a newly instantiated
- instance. unsaved-value defaults to "null", which is perfect for a Long
- identifier type. If we would have used a primitive identitifier property, we would need to specify
+ The unsaved-value attribute is used to specify the identifier value of a newly
+ instantiated instance. unsaved-value defaults to null for nullable
+ types (which is perfect for a Long identifier type), and to 0 for
+ numeric primitives. If we would have initialized our identifier property to -1 in the
+ object's constructor, we would need to specify
- ]]>
+ ]]>
for the Child mapping. (There is also an unsaved-value attribute
@@ -262,8 +273,8 @@ session.flush();]]>
- The following code will update parent and child and insert
- newChild.
+ Now that we have our unsaved-value right, the following code will update
+ parent and child and insert newChild.
Well, thats all very well for the case of a generated identifier, but what about assigned identifiers
and composite identifiers? This is more difficult, since unsaved-value can't
- distinguish between a newly instantiated object (with an identifier assigned by the user) and an object
- loaded in a previous session. In these cases, you will probably need to give Hibernate a hint; either
+ distinguish between a newly instantiated object (with an identifier assigned by the user) and an
+ object loaded in a previous session. In this case, Hibernate will either use the timestamp or version
+ property, or will actually query the second-level cache database to see if the row exists.
-
-
-
-
- define unsaved-value="null" or unsaved-value="negative"
- on a <version> or <timestamp> property
- mapping for the class.
-
-
-
-
- set unsaved-value="none" and explicitly save()
- newly instantiated children before calling update(parent)
-
-
-
-
- set unsaved-value="any" and explicitly update()
- previously persistent children before calling update(parent)
-
-
-
-
+
- none is the default unsaved-value for assigned and composite
- identifiers.
-
-
-
- There is one further possibility. There is a new Interceptor method named
- isUnsaved() which lets the application implement its own strategy for distinguishing
+ There is one further possibility. The Interceptor method named
+ isUnsaved() lets the application implement its own strategy for distinguishing
newly instantiated objects. For example, you could define a base class for your persistent classes.
@@ -361,6 +346,10 @@ public boolean onSave(Object entity,
if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
return false;
}]]>
+
+
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+
@@ -368,16 +357,15 @@ public boolean onSave(Object entity,
Conclusion
- There is quite a bit to digest here and it might look confusing first time around. However, in practice, it
- all works out quite nicely. Most Hibernate applications use the parent / child pattern in many places.
+ There is quite a bit to digest here and it might look confusing first time around. However, in practice,
+ it all works out very nicely. Most Hibernate applications use the parent / child pattern in many places.
We mentioned an alternative in the first paragraph. None of the above issues exist in the case of
<composite-element> mappings, which have exactly the semantics of a parent / child
- relationship. Unfortunately, there are two big limitations to composite element classes: composite elements may
- not own collections, and they should not be the child of any entity other than the unique parent. (However,
- they may have a surrogate primary key, using an <idbag> mapping.)
+ relationship. Unfortunately, there are two big limitations to composite element classes: composite elements
+ may not own collections, and they should not be the child of any entity other than the unique parent.