We recently upgraded to the latest version of NHibernate to make use of many of its new features. There are plenty of good reasons to upgrade including proper support of .Net 2.0, bug fixes, multi queries, new database dialects (including SQL Server 2005) and the ability to use generic types. We anticipated a few issues upgrading NHibernate, and though not the hardest upgrade ever, we did end up with a few more issues than expected. Here’s our experience upgrading to the latest version.
Things they warned you about
The NHibernate guys did a fantastic job writing their migration guide. They warned us that it wasn’t just simply a drop-in replacement and included a number of breaking API changes. Things that we found easy and had been documented included:
- Updating our XML files from
urn:nhibernate-mapping-2.0
tourn:nhibernate-mapping-2.2
- Replacing
outer-join="true"
tofetch="join"
andouter-join="false"
tofetch="select"
- Adding
default-lazy="false"
to each mapping file to deal with the change in default lazy loading behaviour - The HSQL
count
had been upgraded from anint
type to along
type and our code had to cast it to the correct types - Deprecation of the
CreateSQLQuery
with parameters changed toISQLQuery(String)
Things not on the label
- Strange behaviour around the
nosetter.camelcase
access strategy – We had a property exposing a primitive type (string) of a more complex type, given the same field name and we kept getting a null result. It looked like it was trying to set the field using the type of the get property, even though the set should have been using the more complex user type. We fixed this by changing our mappings tofield.camelcase
instead of using thenosetter.camelcase
. - SQL statement logging has changed – Our application listens very carefully to the Log4Net output that NHibernate generates, capturing each SQL statement and its parameters. Previous versions of NHibernate used to log their parameter values separately from their SQL statements, instead they are now logged on the single line. Thankfully our change was contained to two very small classes.
- Replacing
IClassPersister
withIEntityPersister
– We had to add a different constructor to our custom user types (also different from the current documentation), with a signature ofPersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactory required, IMapping mapping
. Additionally we had to implement the new propertyFactory
that came along with this interface. Now I had no idea whereISessionFactoryImplementor
came from. Looking at the code, they had castISessionFactory
in their constructor, and then returned that when the propertyFactory
was called. It is a small inconsistency in their API that we ended up having to duplicate. This would be a problem if you ended up writing your ownISessionFactory
but thankfully we haven’t done that ourselves. There were plenty more methods that we had to implement though none of them were actually very insteresting for the things that we had to do. Our solution: Cast ISessionFactory to ISessionFactoryImplementor and store it in the constructor just to be returned in the property. - IUserType Classes Disappearing – They had warned you that
IUserType
had moved into another namespace though I wasn’t quite clear what you had to do if you were using one of the old versions. In the endnew Int16SqlType()
is replaced bySqlTypeFactory.Int16
,new Int32SqlType()
bySqlTypeFactory.Int32
,new Int64SqlType()
bySqlTypeFactory.Int64
, andnew GuidSqlType()
bySqlTypeFactory.Guid
. I’m guessing it would be the same for any other IUserTypes you may be using. - IUserType New Methods – Four new methods appeared on the interface,
GetHashCode
,Replace
,Assemble
,Disassemble
. Implementing GetHashCode we ended up delegating to our object, replace we delegated to using disassmemble then assemble, and implemented assemble and disassemble using DeepCopy, emulating what NHibernate Types do. It wasn’t really clear to me from documentation or upgrade guides what it should be - NHibernate.HibernateException : Could not instantiate cache implementation – It look like second level caching was now enabled by default and we hadn’t added configuration for it. We kept getting “Second-level cache is not enabled” messages. We disabled it and fixed this problem by explicitly turning it off. The property key is
hibernate.cache.use_second_level_cache
and turn it off by using the valuefalse
.
For the most part, considering how much of the core hibernate functionality had changed, we haven’t had too many issues although it’s still early days. We are noticing slightly different behaviour in the way that the flush semantics seem to be working (maybe auto flush mode is on by default now) though everything is still working quite pleasantly.