This is a short summary of what I had to do to upgrade Hibernate from version 3.2.4 SP1 to 3.6.10 in our reporting/analysis type of application built on an aging Struts 1.2.x / Spring 3.2 (recently upgraded) / Hibernate / MySQL -stack.
I was initially worried I would run into demoralizing amount of problems because although our product is not that large in terms of LOC, Hibernate is used a lot and in many ways (HQL queries, Criteria queries, native MySQL queries, somewhat complex object mappings, etc). However, the upgrade went surprisingly smoothly. The jump from 3.x to 4.x might be a bigger step though but that will be another story.
This case was probably quite a simple upgrade and upgrading other apps might face different issues but hopefully this will help someone.
Here are the steps I took:
- Downloaded the release bundles for 3.6.10
- Threw away the old 3.2.4 hibernate3.jar (and also hibernate-annotations.jar and hibernate-jpa-2.0-api-x.jar which we had because of dependencies from a shared library we use)
- Added the new 3.6.10 hibernate3.jar and the jars from the required/ directory in the 3.6.10 release bundle. Also added the hibernate-jpa-x.jar from under jpa/
At this point I’m getting a compilation error. We have a custom enum UsertType (based on this example), which calls TypeFactory.basic(..), which longer exists. I find a question about this on StackOverflow which fortunately has an answer that contains an updated version of the custom type which works in 3.6.10.
Now the app compiles and deploys to Tomcat without errors. I get to the login page but get an exception thrown in my face immediately on login. The error is
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
The offending query looks something like this:
select f from Foo f left join fetch f.bar left join fetch f.baz as x left join fetch x.y ...
SO comes to my rescue again, someone else has asked about this:
“Use regular join instead of join fetch (by the way, it’s inner by default)”, “As error message tells you, join fetch doesn’t make sense here, because it’s a performance hint that forces eager loading of collection.”
So by changing the last ‘left join fetch’ to just ‘left join’ fixed the issue.
Now I can login and poke around the app manually for a few minutes and to my surprise all the major features seem to work at least on the surface. We have a pretty good integration and system test coverage so if I get those to pass I can be quite confident that nothing major is broken.
First I run the integration tests: 15 failures, all of the type:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.foo.Bar
Turns out there are few HQL queries of the form
select distinct foo, foo.bar from Foo foo inner join fetch foo.bar ...
which have previously for some reason returned
List<Foo> without problems, but now return a list of
Object tuples where the first element is an instance of
Foo and second element is an instance of
Bar. Looking at the query this makes total sense and I’m wondering why it worked before..
Since the code using these queries obviously has not needed the second element before, I change the queries to only select foo and all the failed tests pass.
Next up is system tets. To my surprise everything passes on the first go.
At this point I’m quite sure all the major features work. However there are some smaller less important features that are unfortunately not covered by tests which still need to be checked manually (if there are breakages, tests will be added).