Chapter 1 Example: Parent/Child
Directory
-
About collections
-
Bidirectional one-to-many)
-
Cascading lifecycle)
-
Cascading Update (Using cascading)
update()
)
-
Conclusion
Most of the people who just came into contact with NHibernate started with modeling the parent-child relationship (parent/child type relationship. There are two methods to create a parent-child relationship. The simple and intuitive method is to establish<one-to-many>
FromParent
PointChild
This is especially true for new users. But there is another way to declare Child as<composite-element>
(Combination element ). We can see that the use of one-to-multiple associations in NHibernate is closer to the semantics of the common parent/child relationship than the composite element. Next we will explain how to use bidirectional one to mutual association with cascades to establish an effective and beautiful parent/child relationship. This is not difficult!
About collections
In NHibernate, the object class uses collection as a logical unit of its own, rather than multiple entities. This is very important! It mainly includes the following:
When an object in the collection is deleted or added, the version value of the object that owns the collection increases progressively.
If an object removed from the collection is an instance of value type, such as composite element, the Persistence State of the object will be terminated, the corresponding records in the database will be deleted. Similarly, adding a value type instance to the collection will immediately make it persistent.
On the other hand, if an object (one-to-multiple or multiple-to-multiple) is removed from a one-to-multiple associated collection ), by default, this object will not be deleted. This behavior is completely logical-changing the internal state of an object should not cause the object associated with it to disappear! Similarly, adding an object to the collection object will not make it persistent.
In fact, the default action to add an object to the Collection object is to create a connection between the two objects, and delete the connection when removing the object. This kind of processing is suitable for all situations. The parent-child relationship itself is not suitable in all cases, because whether the Sub-object exists depends on the lifecycle of the parent object.
Bidirectional one-to-many)
Let's start with a simple example. Suppose we want to implementParent
To classChild
.
<set name="Children"> <key column="parent_id" /> <one-to-many class="Child" /></set>
If we run the following code
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();p.Children.Add( c );session.Save( c );session.Flush();
Nhib.pdf generates the following two SQL statements:
This is not only inefficient, but also violates the columnparent_id
Non-empty limit.
The underlying reason is that the objectp
To objectc
(Foreign keyparent_id
) Is not consideredChild
Part of the object status is not inINSERT
Is created. The solution isChild
Set ing at one end.
<many-to-one name="Parent" column="parent_id" not-null="true"
(We also needChild
AddParent
Attribute)
Current entityChild
In the management connection status, we useinverse
Attribute.
<set name="Children" inverse="true"> <key column="parent_id" /> <one-to-many class="Child" /></set>
The following code is used to add a newChild
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();c.Parent = p;p.Children.Add( c );session.Save( c );session.Flush();
Now, there will be only oneINSERT
The statement is executed!
To make things well organized, you canParent
Add oneAddChild()
Method
public void AddChild( Child c ) { this.Children.Add( c ); c.Parent = this;}
AddChild
Simplified the code
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();p.AddChild( c ); // session.Save( c );session.Flush();
Cascading lifecycle)
Call each objectSave()
() The method is very troublesome. We can use cascade to solve this problem.
<set name="Children" inverse="true" cascade="all"> <key column="parent_id" /> <one-to-many class="Child" /></set>
After cascade configuration, the code is simplified:
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();p.AddChild( c );session.Flush();
Note:
Cascade dependencyunsaved-value
Attribute ). Make sure that<id>
Default Value andunsaved-value
Same.
Similarly, when you save or deleteParent
We do not need to traverse children. The following code is deleted from the database.p
And all its children.
Parent p = session.Load( typeof( Parent ), pid ) as Parent;session.Delete( p );session.Flush();
However, this code
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = null;foreach( Child child in p.Children ) { c = child; // only care about first Child break;}p.Children.Remove( c );c.Parent = null;session.Flush();
Will not be deleted from the databasec
; It will only deletep
(And may cause a violationNOT NULL
Constraints, in this example ). You need to callChild
OfDelete()
Method.
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = null;foreach( Child child in p.Children ) { c = child; // only care about first Child break;}p.Children.Remove( c );c.Parent = null;session.Delete( c );session.Flush();
In our example, if we specify that there is no parent object, the sub-object should not exist. If we remove the sub-object from the collection, we actually want to delete it. To achieve this requirement, you must usecascade="all-delete-orphan"
.
<set name="Children" inverse="true" cascade="all-delete-orphan"> <key column="parent_id" /> <one-to-many class="Child" /></set>
Note: Even if you specifyinverse="true"
The cascade operation is still executed when the collection is traversed. To insert, delete, and update sub-objects through cascading, you must add them to the collection and only callChild.Parent
Setter is not enough.
Cascading Update (Using cascading)
update()
)
Suppose we start fromISession
AnParent
Object, the user interface has modified it, and we want to call it in a new ISessionUpdate()
To update it. ObjectParent
A set of sub-objects is included. Since cascade update is enabled, nhib.pdf needs to know which sub-objects are new and which are existing in the database. Let's assume thatParent
AndChild
The object's identity property type isSystem.Int32
. NHibernate uses the value of the identifier property to determine which sub-objects are new. (You can also use the version or timestamp attribute)
unsaved-value
Attribute is used to indicate the identity property value of the new instance. The default value is "null". For. net Value Type (ValueTypes), this is not a good default value, so you need to provideunsaved-value
.
If we use the original type as the identification type, we must write the following When configuring the Child class ing:
<id name="Id" type="Int64" unsaved-value="0">
ForChild
Ing. (There are alsounsaved-value
Attribute is provided to the version and timestamp attributes (property ing)
The following code is updated:parent
Andchild
Object, and insertnewChild
Object.
// Parent and child were both loaded in a previous page ious sessionparent. addChild (child); Child newChild = new Child (); parent. addChild (newChild); session. update (parent); session. flush ();
Okay. This is very convenient for Automatic Generation of identifiers. But what about self-assigned identifiers and composite identifiers? This is a little troublesome, becauseunsaved-values
The new object (ID specified by the user) and the previous object cannot be distinguished.ISession
The object to load. In this case, you may need to give NHibernate some tips before calling update (parent:
In this class<version>
Or<timestamp>
Attribute ingunsaved-value="null"
Orunsaved-value="negative"
.
ExecuteUpdate( parent )
Previously, Setunsaved-value="none"
And explicitly callSave()
Create a new sub-object in the database
ExecuteUpdate( parent )
Previously, Setunsaved-value="any"
And explicitly callUpdate()
Update mounted sub-objects
none
It is a self-assigned ID and a composite IDunsaved-value
.
Conclusion
This problem is often confusing for new users. It is indeed not easy to digest. However, after some practices, you will feel more and more comfortable. The parent-child object mode has been widely used in nhib.pdf applications.
In the first section, we mentioned another solution. The semantics of the composite element is equivalent to that of the parent-child relationship, but we have not discussed it in detail. Unfortunately, composite elements have two major limitations: composite elements cannot have collections, and they cannot be sub-objects of any other entity except for the unique parent object. (However, by using<idbag>
Ing, which may have the proxy primary key .)