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
default-lazy="false"to each mapping file to deal with the change in default lazy loading behaviour
- The HSQL
counthad been upgraded from an
inttype to a
longtype and our code had to cast it to the correct types
- Deprecation of the
CreateSQLQuerywith parameters changed to
Things not on the label
- Strange behaviour around the
nosetter.camelcaseaccess 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 to
field.camelcaseinstead of using the
- 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.
IEntityPersister– We had to add a different constructor to our custom user types (also different from the current documentation), with a signature of
PersistentClass model, ICacheConcurrencyStrategy cache, ISessionFactory required, IMapping mapping. Additionally we had to implement the new property
Factorythat came along with this interface. Now I had no idea where
ISessionFactoryImplementorcame from. Looking at the code, they had cast
ISessionFactoryin their constructor, and then returned that when the property
Factorywas 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 own
ISessionFactorybut 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
IUserTypehad 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 end
new Int16SqlType()is replaced by
SqlTypeFactory.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,
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_cacheand turn it off by using the value
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.