One of NHiberantes features that I haven’t seen mentioned in the documentation or in blogs concerns the the two methods on ISession SaveOrUpdateCopy and Merge.
Lets say we have an backend (i.e. application server) that has an operation called UpdateOrder.
The UpdateOrder message contains a complete order. The normal scenario here is that the backend translate the order contained in the message to a domain model that is then persisted to the database via NHibernate. The problem with an update scenario like this is that the order that is coming in from the client could have missing order lines. If you use the normal ISession SaveOrUpdate method the order line that was removed on the client and therefore missing in the UpdateOrder message will not be deleted in the database.
Why won’t the missing order line be deleted from the db? Well consider this normal update scenario:
Here the order line is removed on the instance that is already attached to an open NHiberante session. In this case SaveOrUpdate will work perfectly because NHiberante can track the removal of the order line.
Consider this case (that represents the client –> backend scenario I mentioned above)
Here we try to save a detached instance which means that NHibernate’s dirty tracking and tracking of collection removals will not work.
How do you solve this problem? One approach is to fetch the order from the db and do a manual merge of the changes. The current system that I am working on has a data access layer that is using LinqToSql and there are many, many update scenarios as described above. The amount of code to manually merge and figure out what has happened with all relations (added / removed order lines for example) is quite substantial.
For the last month we have been bit by bit migrating the data access layer to NHibernate. First I thought that this issue of updating detached objects would be a problem that we still needed to solve manually but then I discovered SaveOrUpdateCopy and Merge. These two functions does exactly what the old DAL did manually, that is before the update it fetches the persisted object from the db and then merges all changes from the detached instance into the persisted instance automatically, including orphaned child deletions!
In the above code I modify an order line and remove another. Both operations are on a detached object. Then using SaveOrUpdateCopy we get this:
NHibernate fetches the order (in one statement by joining in the order lines), then performs the merge, figures out that one order line is updated and one is removed and issues the correct database calls. Is it just me but isn’t this great??? This will literally save thousands of lines of code!
The ISession.Merge function basically does the same (from what I can tell). I am not sure really what separates them, except that there is cascade option named “merge” that you can set on relations to control how cascades should be propagated during merge operations.
Here is the API doc for Merge:
Copy the state of the given object onto the persistent object with the same
identifier. If there is no persistent instance currently associated with
the session, it will be loaded. Return the persistent instance. If the
given instance is unsaved, save a copy of and return it as a newly persistent
instance. The given instance does not become associated with the session.
This operation cascades to associated instances if the association is mapped
with cascade="merge". The semantics of this method are defined by JSR-220.
I think SaveOrUpdateCopy is something that has exited in NHiberante for all time and Merge is something added in 2.1 (clearly something ported from the hibernate). Anyway I am very glad that NHibernate has this ability because writing and handling the merge operation manually is very boring code to write!