Logo

NHibernate

The object-relational mapper for .NET

NHibernate Mapping - <any/>

Sometimes, well known associations just don’t cut it. We sometimes need to be able to go not to a single table, but to a collection of table. For example, let us say that an order can be paid using a credit card or a wire transfer. The data about those are stored in different tables, and even in the object model, there is no inheritance association them.

From the database perspective, it looks like this:

As you can see, based on the payment type, we need to get the data from a different table. That is somewhat of a problem for the standard NHibernate mapping, which is why we have <any/> around.

Just to close the circle before we get down into the mapping, from the object model perspective, it looks like this:

image

In other words, this is a non polymorphic association, because there is no mapped base class for the association. In fact, we could have used System.Object instead, but even for a sample, I don’t like it.

The mapping that we use are:

<class name="Order"
			 table="Orders">

	<id name="Id">
		<generator class="native"/>
	</id>

	<any name="Payment" id-type="System.Int64" meta-type="System.String" cascade="all">
		<meta-value value="CreditCard" class="CreditCardPayment"/>
		<meta-value value="Wire" class="WirePayment"/>
		<column name="PaymentType"/>
		<column name="PaymentId"/>
	</any>

</class>

<class name="CreditCardPayment"
			 table="CreditCardPayments">
	<id name="Id">
		<generator class="native"/>
	</id>
	<property name="IsSuccessful"/>
	<property name="Amount"/>
	<property name="CardNumber"/>
</class>

<class name="WirePayment"
			 table="WirePayments">
	<id name="Id">
		<generator class="native"/>
	</id>
	<property name="IsSuccessful"/>
	<property name="Amount"/>
	<property name="BankAccountNumber"/>
</class>

Pay special attention to the <any/> element. Any <meta-value/> declaration is setting up the association between the type as specified in the PaymentType column and the actual class name that it maps to. The only limitation is that all the mapped class must have the same data type for the primary key column.

Let us look at what this will give us:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var order = new Order
	{
		Payment = new CreditCardPayment
		{
			Amount = 5,
			CardNumber = "1234",
			IsSuccessful = true
		}
	};
	session.Save(order);
	tx.Commit();
}

Which produces:

image

image

And for selecting, it works just the way we would expect it to:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var person = session.Get<Order>(1L).Payment;
	Console.WriteLine(person.Amount);
	tx.Commit();
}

The generated SQL is:

image

image

An interesting limitation is that you cannot do an eager load on <any/>, considering the flexibility of the feature, I am most certainly willing to accept that limitation.


Posted Mon, 20 April 2009 06:29:00 PM by Ayende
Filed under: mapping

comments powered by Disqus
© NHibernate Community 2024