In the database world, we have three kind of associations: 1:m, m:1, m:n.
However, occasionally we want to have a one to one relationship. We could simulate it easily enough on the database side using two many to one relations, but that would require us to add the association column to both tables, and things gets… tricky when it comes the time to insert or update to the database, because of the cycle that this creates.
NHibernate solves the problem by introducing a one-to-one mapping association, which allow you to define the two relationships based on a single column in the database, which controls the two way association.
<one-to-one name="PropertyName" (1) class="ClassName" (2) cascade="all|none|save-update|delete" (3) constrained="true|false" (4) fetch="join|select" (5) property-ref="PropertyNameFromAssociatedClass" (6) access="field|property|nosetter|ClassName" (7) />
1, 2, 3, 6, 7 were all discussed elsewhere, so I’ll skip them and move directly to showing how this can be used.
We have the follow object model:
And the database model:
Note that while in the object model we have a bidirectional mapping, in the database we have only a single reference on the employees table. In the relational model, all associations are naturally bidirectional, but that is not true on the object model. In order to bridge this inconsistency, we map them as:
<class name="Employee" table="Employees"> <id name="Id"> <generator class="native"/> </id> <property name="Role"/> <many-to-one name="Person" unique="true" column="Person"/> </class> <class name="Person" table="People"> <id name="Id"> <generator class="native"/> </id> <property name="Name" /> <one-to-one name="Employee" class="Employee"/> </class>
We have a unique many-to-one association from Employee to Person, but a one to one from Person to Employee. This will reuse the many-to-one association defined in the Employee mapping.
Let see how this works for saving and loading the data:
using (var session = sessionFactory.OpenSession()) using (var tx = session.BeginTransaction()) { var person = new Person { Name = "test", }; var employee = new Employee { Person = person, Role = "Manager" }; person.Employee = employee;
session.Save(person);
session.Save(employee); tx.Commit(); } // person to employee using (var session = sessionFactory.OpenSession()) using (var tx = session.BeginTransaction()) { var employee = session.Get<Person>(1).Employee; Console.WriteLine(employee.Role); tx.Commit(); } // employee to person using (var session = sessionFactory.OpenSession()) using (var tx = session.BeginTransaction()) { var person = session.Get<Employee>(1).Person; Console.WriteLine(person.Name); tx.Commit(); }
And the SQL that would be generated would be:
This is quite interesting. We can see that we insert the entities as we expect, but when we pull a person out, we do a join to the employee, to get the one-to-one association. For that matter, even in the second scenario, we do a join to get the associated employee.
The reason that we have to do it is quite interesting as well. NHibernate makes some guarantees about the way the object model and the database model map to one another. And one of those guarantees is that if there is no association in the database, we will get back a null in the object model.
Generally, this works very well, since we can tell whatever an association exists or not using the value in the table (for many-to-one associations). But for one-to-one association, if we want to keep this guarantee, we have to check the associated table to verify if we need to have a null or a proxy there. That is somewhat annoying, but we can get around that by specifying constrained=”true”. This tell NHibernate that in this case, whenever there is a Person, there must also be a matching Employee value. We can specify it like this:
<one-to-one name="Employee" constrained="true" foreign-key="none" class="Employee"/>
Something else to note is that we must specify this with foreign-key=”none”, because otherwise NHibernate’s Schema Export feature would create two foreign keys for us, which would create a circular reference that wouldn’t allow us to insert anything into the database.
When setting this, we can see that there is a dramatic change in NHibernate’s behavior:
Instead of generating joins, NHibernate now uses standard selects to get the data. And we don’t have to pre-populate the information on loading the entity, we can delay that as we usually do with NHibernate.
And the last thing that we will explore for <one-to-one/> is the fetch attribute. It defaults to select, so we have already seen how that works, but when we set fetch=”join”, we get an interesting flashback. Well, almost:
Again, we use a join to get the value upfront, but since we are now using constrained=”true”, we can use an inner join instead of a left outer join, which is more efficient in most cases.