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 to urn:nhibernate-mapping-2.2
  • Replacing outer-join="true" to fetch="join" and outer-join="false" to fetch="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 an int type to a long type and our code had to cast it to the correct types
  • Deprecation of the CreateSQLQuery with parameters changed to ISQLQuery(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 to field.camelcase instead of using the nosetter.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 with 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 Factory that came along with this interface. Now I had no idea where ISessionFactoryImplementor came from. Looking at the code, they had cast ISessionFactory in their constructor, and then returned that when the property Factory 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 own ISessionFactory 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 end new Int16SqlType() is replaced by SqlTypeFactory.Int16, new Int32SqlType() by SqlTypeFactory.Int32, new Int64SqlType() by SqlTypeFactory.Int64, and new GuidSqlType() 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, 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 value false.

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.