One to many relationships in a relational model is probably the most common kind of relationship used in a relational database. Typically, the table in the “many” side of the relationship contains a special column in which each row contains the Id of the corresponding row in the table at the ‘one’ side.
Even though this is very straight-forward in the relational database model, there are many nuances as how to map it to the object-oriented model. These nuances confuses many developers, especially those new to NHibernate. In this post I’ll try to explain each of these nuances one by one, so the next time you’ll need to map a one-to-many relationship you’ll understand what you’re doing.
If you don’t care to not understand what you’re doing, but only want a quick reference on what to use when, you can just directly to the end of this post.
Which side contains a reference to the other side?
First, let’s distinguish between 3 major kinds on one-to-many relationships, as they are modeled in code:
- The ‘many’ side points to the ‘one’ side (similar to the relational model). Note that the Customer doesn’t hold a collection of its Orders. I like to refer to these kind of relationships as “foreign key relationships”
- The ‘one’ side contains a property which is a collection (e.g. IList) that references all of the elements in the ‘many’ side. I like to refer to this kind of relationships as “parent/child relationships” (the ‘one’ has many children)
- The last kind is actually the combination of the first two kinds, and it’s referred to as a “bi-directional relationship”. In this kind, the parent contains a collection of its children (as in the “parent/child relationship”), and also each children has a reference its parent (like in the “foreign key relationship”).
As far as I can tell, the 2nd kind (“parent/child relationship”) is the most common relationship used in code.
To map the “foreign-key relationship” between Order and Customer you need to specify the relationship on the Customer property of the Order class (the ‘many’ side) as follows:
<class name="Order" table="Orders" > …
<many-to-one name="Customer" column="CustomerSSN" foreign-key="SSN"/>
To map the “parent/child relationship” between Order and OrderLine you need to specify the relationship on the Lines collection property of the Order class (the “one” side) as follows:
<class name="Order" table="Orders" > ... <bag name="Lines"> <key column="OrderId" /> <one-to-many class="OrderLine"/> </bag> </class>
As you can guess, to map a bidirectional relationship, you have to specify both the many-to-one element on the ‘many’ side, and the one-to-many element on the ‘one’ side. However, if your code changes the association between the 2 objects, it’s your responsibility to make sure that the 2 sides of the relationships are in-sync: Whenever you add a child to the parent’s collection, you must not forget to update the property that references the parent from the child.
In addition, you must tell NHibernate which side of the relationship to use in order to track if the association has changed. You do it by specifying inverse=”true” on the side that you want NHibernate to ignore.
The 2nd distinction I like to make, is the distinction between Entities and Components:
Generally speaking, an entity is the smallest unit that has a meaning by itself, and not just as part of something bigger. For example, both the Customer and the Order are entities: a Customer can exist without its orders; An Order, even though it must be associated with a Customer, has a complete meaning by itself. On the other hand, an OrderLine is not very meaningful without its containing Order. Therefore, the OrderLine will probably be a Component of the Order entity.
This definition is somewhat abstract and vague, as it depends on the context. To be more concrete, I usually use the following question to determine whether a class is an entity or a component: Does the application allow the user to search for a particular instance of this class? (or, if we’re in early stages of the development: Is it likely that we’ll want to allow the user to search…) If the answer is “Yes” then this an entity, otherwise it’s a component.
As far as NHibernate is concerned, Entities must declare an Id property/field (aka POID – Persistent Object Id). NHibernate uses this Id to correlate between instances of the the class in memory and the row in the database. Components are only defined inside the context of their containing entity, and can’t declare an Id.
To define the Order and OrderLine as two different entities, declare each of them in separate <class …> elements as shown previously.
To define the OrderLine as a component of the Order class, you need to declare the OrderLine class in a <Composite-element…> element inside the Order class element, instead of the <one-to-many…> element:
<class name="Order" table="Orders" > ... <bag name="Lines"> <key column="OrderId"/> <composite-element class="OrderLine"> <!-- properties of OrderLine come here--> </composite-element> </bag> </class>
Because components do not have Ids, in case that some child object has changed, NHibernate can’t determine which row in the database need to be updated. Therefore the only way NHibernate can update the database to reflect the changes is to delete all of the children of that parent, and then re-insert all of the children one by one. This is a significant drawback in terms or performance, and therefore NHibernate came up with a special type of collection called idBag.
When you declare the collection as idBag instead of Bag, you have to tell NHibernate which column in the children’s table uniquely identifies a row. Note that this column is not associated with a property on the class! This is how it looks like:
<idbag name="Lines"> <collection-id column="Id" type="Int32"> <generator class="identity"/> </collection-id> <key column="OrderId" not-null="true" /> <composite-element class="OrderLine"> <!-- properties of OrderLine come here--> </composite-element> </idbag>
Especially in parent-child relationships, when you save, update or delete the parent, usually you want the children to be saved, updated or deleted as well. Note that for components, this happens automatically, but often you want this to be the case even if the children are entities.
Notice that by default, if you remove a child element from its parent, NHibernate can’t determine whether you intend to delete the child element, or only break the association, leaving the child entity without a parent. (Recall the definition of an Entity: it’s has a meaning by its own). Of course that you can call ISession.Delete(child) whenever you remove a child from its parent, but that would be very cumbersome, so NHibernate allows us to specify that we want to delete the children that were removed from their parent (that’s why they called Orphans), by specifying cascade=”all-delete-orphan” on the <bag…> element
Given a parent/child relationships, if you create a new parent entity with one or more children, and then try to save this parent entity (by calling ISession.SaveOrUpdate for example) NHibernate works as follows:
- It persists the parent entity by executing an INSERT statement
- If cascade is set to save-update, all, or all-delete-orphan, NHibernate persists each of the child elements by executing an INSERT statement for each of the children. However, because the relationship is not part of the child entity (as it’s defined as a bag element on the parent), the column that defines the key of the bag doesn’t participate in the INSERT and remains NULL.
- It updates the key column of the relationship in the child table with the ID of the parent. (In other words, it associates the children with the parent)
Beside the fact that a redundant round-trip to the database is required in this case, it also requires that the key column would allow nulls, otherwise the 2nd insert will fail!
In foreign-key and bi-directional relationships this problem does not exist because the key column is part of the child entity.
The following table summarizes the consequences of each mapping, and how it affects the various operations (update, add & remove). Not all the combinations are useful, but I show it for the aim to help you understand how NHibernate treat each property. The right-most column helps you decide when to use it in a real application:
|Mapping||Updating a child||Removing a child from the parent’s collection||Adding a new child to the parent’s collection||When to use?|
|<Bag> with <Composite-element>||Delete all children and re-insert them||Delete all children and re-insert them||Delete all children and re-insert them||Never (unless you want the simplest mapping, and performance of update, remove or add are not important)|
|<idbag> with <Composite-element>||single update||single delete||single insert||Whenever the children are not entities|
|<bag> with <one-to-many>, cascade=”none”||single update||Detach (set NULL in the key column)||Exception: the child entity is not persisted!||Never|
|<bag> with <one-to-many>, cascade=”all-delete-orphan”||single update||Detach and then delete (2 SQL statements)||Insert and then attach||When the child is an entity and you prefer simpler code over performance|
|bi-di relationship with
<bag invert=”true”> and cascade=”none”
|single update||Nothing happens!||Nothing happens!||Never|
|bi-di relationship with
<bag invert=”true”> and cascade=”all-delete-orphan”
|single update||Single delete||Single insert||Whenever the children are entities. Note that you must take care of maintaining both sides of the relationship in sync|
|<many-to-one>||single update||N/A||N/A||When the ‘one’ doesn’t hold a collection of its ‘many’ (foreign-key relations0|
In most cases, an <idbag> with <composite-element> do the work in the simplest way. However, if you need that the children be entities of their own, than you must use a bi-directional relationship with cascade=”all-delete-orphan”. This solution has the drawback of having to update the parent property of the child object whenever an item is added or removed from the collection of its parent, but it’s the most efficient.