Series Articles
[Nhibernate] Architecture
[NHibernate] Isessionfactory Configuration
[NHibernate] Persistence class (persistent Classes)
[NHibernate] O/R Mapping Basics
[NHibernate] Collection Class (collections) mappings
[NHibernate] Association mappings
Introduction
Most people who have just touched nhibernate start with the modeling of the parent-child relationship (Parent/child type relationship). There are two ways to model a parent-child relationship. The simpler and more intuitive approach is to establish the <one to many> relationship between the entity class parent and child, which points to the child from the parent, especially for novices. But there is another way to declare child as a <composite-element> (composition element). You can see that using a one-to-many association in NHibernate is closer to the semantics of the usual parent/child relationship than the composite element. Below we will explain how to use a two-way cascade one-to-many association (bidirectional one to many association with Cascades) to establish an effective, graceful parent/child relationship.
About collections
Under NHibernate, the entity class takes collection as one of its own logical units, not multiple entities that are accommodated. This is very important! It is mainly embodied in the following points:
- When you delete or increase the version value of an entity object in collection, it is incremented.
- If an object removed from collection is an instance of a value type (value of type), such as the composite element, then the persisted state of the object will be terminated and its corresponding record in the database will be deleted. Similarly, an instance that wants to collection add a value of type will make it immediately persistent.
- On the other hand, if you remove an entity from a one-to-many or many-to-many association collection (a pair of multiple one-tomany or many-to-many many-to-many associations), the object is not deleted by default. This behavior is perfectly logical-changing the internal state of an entity should not cause the entities associated with it to disappear! Similarly, adding an entity to collection does not make it persistent.
In fact, the default action of adding an entity to collection is simply to create a connection between the two entities, and remove the connection only when it is removed. This treatment is appropriate for all situations. What is not appropriate for all cases is actually the parent-child relationship itself, because the object has a lifetime dependent on the parent object.
Bidirectional one-to-many relationship (bidirectional one-to-many)
Let's start with a simple example, assuming that you want to implement a one-to-many relationship from the parent class to the child of the class.
1 <Setname= "Children">2 <Keycolumn= "parent_id" />3 <One-to-manyclass= "Child" />4 </Set>
If we run the following code
1 typeof as Parent; 2 New Child (); 3 P.children.add (c); 4 session. Save (c); 5 session. Flush ();
NHibernate will produce the following two SQL statements:
- A ineset statement that is used to create the database record corresponding to object C.
- An UPDATE statement that is used to create a connection from object p to object C.
This is not only inefficient, but also violates the limit of column parent_id non-null.
The underlying reason is that the connection of the object P to object C (foreign key parent_id) is not considered part of the state of the child object and is not created at insert time. The workaround is to set the mapping at the child end.
1 < name= "Parent" column= "parent_id" not-null= "true"
(We also need to add the parent property for class child)
Now that the entity child is managing the state of the connection, in order for the collection not to update the connection, we use the inverse property.
1 <Setname= "Children"Inverse= "true">2 <Keycolumn= "parent_id" />3 <One-to-manyclass= "Child" />4 </Set>
The following code is used to add a new child
1 typeof as Parent; 2 New Child (); 3 c.parent = p; 4 P.children.add (c); 5 session. Save (c); 6 session. Flush ();
Now, only one INSERT statement will be executed!
To get things organized, you can add a Addchild () method to the parent.
1 Public void 2{3This . Children.add (c); 4 This ; 5 }
Addchild simplifies the code.
1 typeof as Parent; 2 New Child (); 3 P.addchild (c); // 4 session. Save (c); 5 session. Flush ();
Cascade life cycle (cascading lifecycle)
Invoking the Save () method on each object is cumbersome, and we can use cascading to solve the problem.
1 <Setname= "Children"Inverse= "true"Cascade= "All">2 <Keycolumn= "parent_id" />3 <One-to-manyclass= "Child" />4 </Set>
Once the configuration is cascaded, the code is simplified:
1 typeof as Parent; 2 New Child (); 3 P.addchild (c); 4 session. Flush ();
Attention
Cascade four parts dependent on the Unsaved-value property (attribute). Make sure that the default value of the <id> property is the same as Unsaved-value. Similarly, we do not need to handle children when saving and deleting the parent. The following code removes p and all of its children from the database.
1 typeof as Parent; 2 session. Delete (p); 3 session. Flush ();
However, this piece of code
1Parent p = Session. Load (typeof(Parent), PID) asParent;2Child C =NULL;3 foreach(Child childinchP.children)4 {5c = child;// First child6 Break;7 }8 P.children.remove (c);9C.parent =NULL;TenSession. Flush ();
C is not removed from the database, it only deletes the connection to P (and causes the NOT NULL constraint to be violated, in this case). You need to explicitly call the child's Delete () method.
1Parent p = Session. Load (typeof(Parent), PID) asParent;2Child C =NULL;3 foreach(Child childinchP.children)4 {5c = child;// First child6 Break;7 }8 P.children.remove (c);9C.parent =NULL;Ten session. Delete (c); OneSession. Flush ();
In our case, if we stipulate that there is no parent object, the child object should not exist, and if the child object is removed from the collection, we actually want to delete it. To achieve this requirement, you must use Cascade= "All-delete-orphan".
1 <Setname= "Children"Inverse= "true"Cascade= "All-delete-orphan">2 <Keycolumn= "parent_id" />3 <One-to-manyclass= "Child" />4 </Set>
Note: Even if inverse= "true" is specified in the map of the collection side, the cascade operation will still execute when traversing collection. If you want to cascade to insert, delete, update the child object, you must call it into collection, only call Child.paent setter is not enough.
Cascading updates (Using cascading update ())
Suppose we loaded a parent object from ISession, the user interface modifies it, and then we want to update it by calling Update () in a new isession. The object parent contains a collection of child objects, and since cascading updates are turned on, NHibernate needs to know which child objects are new and which are already present in the database. We assume that the identity property of the parent and child objects is of type System.Int32. NHibernate uses the value of the identity property to determine which child objects are new. (You can also use the version or timestamp property)
The Unsaved-value property is used to represent the identity property value of the new instance, the default is "null", and for. NET value type (ValueTypes) This is not a good default value, so you need to be very high unsaved-value.
If we use the original type as the identity type, we must write when we configure the Child class mapping:
<name= "Id" type= "Int64" unsaved-value= "0" >
For child mappings (there are also unsaved-value properties (attribute) that are provided to version (versions) and timestamp (timestamp) property mappings). The following code updates the parent and child objects and inserts the Newchild object.
1 // parent and child were both loaded in a previous page IOUs session 2 parent. AddChild (child); 3 New Child (); 4 parent. AddChild (newChild); 5 session. Update (parent); 6 session. Flush ();
OK, it's easy to do this for auto-generating identities, but what about self-allocating identities and composite identities? This is a bit of a hassle, because unsaved-values cannot differentiate between new objects (identities are user-specified) and the previous ISession-loaded objects. In this case, you may need to give nhibernate some hints before you wait for update (parent):
- Define unsaved-value= "null" or unsaved-value= "negative" on <version>or <timestamp> attribute mappings for this class.
- Before the update (parent) is performed on the parent object, set unsaved-value= "None" and explicitly call Save () to create a new child object in the database.
- Before updating (parent) is performed on the parent object, the unsaved-value= "any" is set and the explicit call to update () updates the already mounted child object None is the default value for the Unsaved-value that automatically assigns the identity and compound identity.
Summarize
This problem often confuses the novice, it is really not easy to digest. However, after some practice, you will feel more and more handy. The parent-child object pattern has been widely used in nhibernate applications.
In the first paragraph we have mentioned another scenario. The semantics of compound elements are equivalent to parent-child relationships, but we have not discussed them in detail. Unfortunately, there are two significant limitations to compound elements: Composite elements cannot have collections, and they can no longer be child objects of any other entity except for the unique parent object. (However, they may have a proxy primary key by using the <idbag> mapping.) )
This article is from "NHibernate Chinese document"