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 <literal>update()</literal> + Cascades and <literal>unsaved-value</literal> - 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.