NHibernate Gotchas - Living With Legacy Databases
As we've used NHibernate more and more we've begun to hit against issues, particularly when working against a "legacy" database. So if you are working against a green fields database then your probably fine, you can design from the domain down but if you're not then I think two issues in particular are worth bearing in mind:
- Inability to map inheritance at the component level.
- Inability to map unidirectional many-one in a satisfactory way in some cases.
Before I continue I should say two things. The first is that the class names are unimportant, I made them up as I don't want to use real class names or examples from our project. Secondly I am not going to provide solutions, I don't know if it’s even possible for an ORM solution to handle these issues better than NHibernate does but you should still know about them.
Components And Inheritance
NHibernate component mappings are great and can improve your domain model but there are limits.
Take the example of an Customer table that has 50+ columns, don't ask me why but it does. You want to redesign the table but thats something to be planned and executed carefully, DB refactoring is painful especially when you have multiple legacy systems and reports/DTS's working against the database.
So for now you want to design your domain model to have a core Customer class and then lots of little inheritance hierarchies off it. For example if the Customer came directly from your Web site then maybe 5 fields in the database are used and if they came indirectly its a different 10.
In each of the two cases you have different behavior and certainly different validation requirements so you decide to have an ICustomerSource interface (making this up as I go) with two subclasses called DirectCustomerSource and ExternalCustomerSource. The Customer will then have a reference to an ICustomerSource which it will be passed in it constructor.
The problem is you cannot do this because your mapping would have each subclass of ICustomerSource mapped as a component, meaning that when the Customer is saved so should the ICustomerSource. The problem is that the component mapping does not support mapping inheritance so, as far as I know, you are slightly stuffed.
You could certainly subclass Customer and get around it that way but that doesn't always work and certainly wouldn't work for us because we actually want to have multiple little class hierarchies hanging off Customer.
I've logged this "issue" in the NHibernate JIRA.
Unidirectional Many-One Associations
Let’s say we have Order and OrderItems. The Order is the root of an aggregate containing OrderItems. In this case we might actually want to do this in the domain by having the Order have a collection of OrderItems. Maybe not for this situation but we often do.In the domain we want to make the association unidirectional though, we can go from a Order to an OrderItem but we don't want to do the reverse association.
Our database design has the ID of the Order as a foreign key in the OrderItem table. Many people see this as a valid database design and so it’s quite possible this is the way you have it done.
The problem is this isn't going to work unless we do one of two things:
- Make the foreign key in the OrderItem table nullable.
- Add the association from OrderItem to Order.
This topic is discussed in depth in the forums:
- Describes why Hibernate works that way.
- Another post with more context.
- Yet another post on it.
Of course you can just use one of the work arounds but its worth knowing that this issue exists as it can catch you out. I've logged an issue in the NHibernate Jira about it.
Summary
Neither issue should convince you that NHibernate is a dead loss, it is still magic but it does have limits and you need to be aware of them.