NHibernate Forge
The official new home for the NHibernate for .NET community

NHibernate Mapping - <one-to-one/>

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:

image

And the database model:

image

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:

image

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:

image

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:

image

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.


Posted abr 19 2009, 03:31 a.m. by Ayende
Filed under:

Comments

Eddie Gems wrote re: NHibernate Mapping - <one-to-one/>
on 04-29-2009 9:03

In your examples we map Person property of Employee class  via column PERSON of EMPLOYEE table.So when we want to get employee from DB we create such query: select * from EMPLOYEE e join PERSON p on p.Id = e.Person. Right? I think yes.

So. When we load person + employee we need to use such query: select * from PERSON p left join EMPLOYEE e on e.Person = p.Id, but in your mapping for Person class we saw that Employee mapped to Person via primary keys, i.e. Employee.Id = Person.Id, and I think this is an error.

And in your article is nothing about "property-ref" attribute. As I understand from documentation it's destination is a little bit odd for me. I try to explain/

I case of your mappings I think that the right way to define mapping for one-to-one relation to employee from Person class would:

<one-to-one name="Employee" class="Employee" property-ref="Person" />

but what if we don't want property Person at the Employee class? What we need specify in the "property-ref" attribute?

My opinion is that we need "column" attribute for on-to-one element at the mapping.

Cheers )

Ayende wrote re: NHibernate Mapping - <one-to-one/>
on 04-29-2009 9:11

Eddie,

If you want a column attribute, you might as well use the (much) simpler many-to-one.

GLMnet wrote re: NHibernate Mapping - <one-to-one/>
on 01-18-2011 0:36

I agree with Eddie, this example should be fixed. It is clear in the second sql sreenshot that Person column is not used:

SELECT .. FROM People person0_ WHERE person0.Id = 1

// We get a person which id is 1

SELECT .. FROM Employees employee0_ WHERE employee0.Id = 1

// now that 1 is the Person id, but is "joined" with employee id, so this works by coincidence.

The query should be:

SELECT .. FROM Employees employee0_ WHERE employee0.Person = 1

To get NH do that you should do the property-ref thing.

GLMnet wrote re: NHibernate Mapping - <one-to-one/>
on 01-18-2011 0:36

I agree with Eddie, this example should be fixed. It is clear in the second sql sreenshot that Person column is not used:

SELECT .. FROM People person0_ WHERE person0.Id = 1

// We get a person which id is 1

SELECT .. FROM Employees employee0_ WHERE employee0.Id = 1

// now that 1 is the Person id, but is "joined" with employee id, so this works by coincidence.

The query should be:

SELECT .. FROM Employees employee0_ WHERE employee0.Person = 1

To get NH do that you should do the property-ref thing.

Powered by Community Server (Commercial Edition), by Telligent Systems