Sunday, February 8. 2009
Migrating an existing Java project for use with Hibernate is a difficult process. And yet I'm not sure if it is worth the work at all.
Some years ago I started a Java project with direct database access using JDBC and SQL. I designed my object model and a relational database schema. Both were optimized independenlty to work optimal in their environment. In order to connect the object world with the relational world, I wrote a clean storage layer, which was responsible for communicating with the database. This worked pretty good. But when Hibernate was becoming more and more popular, I started thinking about migrating to a Hibernate based object mapping.
Well there are some good reasons to use Hibernate:
- Simplifying the database access.
- Getting more independent from database vendors.
- Becoming more flexible when refactoring or enhancing the data model. Especially since there is a good IDE support for refactoring the Hibernate mappings.
So a few days ago I created a project branch and started to work on that. Quickly I realized that this would become much more difficult than expected at first.
I used Eclipse with the Hibernate Tools plugin to generate a set of mapping files based on the existing database schema. Ok, that was an easy start. For every table Hibernate Tools generated a corresponding mapping and every mapping must be associated with a Java class. For most tables this was no problem, but there were a couple mapping tables where I had no suitable class for. In my own code, these tables were joined, but had never its own representation. Now I had the choice to either change my object model to align it with the Hibernate mapping or to tweak the mapping files to use joins either. Since changing the object model would change the whole code base, it was clear that I had to change the mapping. Beside this I was happy with my model and don't like the idea to change it. Later I realized that this would become impossible in one case.
For now I was a bit overwhelmed from the complexity of the 20 mappings generated by Hibernate Tools. So I picked out one by one and started to write DAOs for them. In a first step I tried to load each class stand alone. This was already some work since the conversion of the database types did not always match the types I used in my classes. In my storage layer I have full control over the conversions. But now Hibernate does it automatically with no easy way to change it. This leads to the problem that a database timestamp is converted to the java.sql.Date type. But the SQL date type does not make sense for me. I solved this problem by adding dedicated getters and setter just for Hibernate, where I convert the type immediately into the correct one.
Another problem was that not all tables contain surrogate keys as suggested by Hibernate. For some tables it was more useful to use composite-ids. For these tables I had to write select and update statements. Here I used HQL and the query by example feature from Hibernate and found both as useful.
So after two days I could read and write all objects standalone from the database. Now the tricky part started. I had to model the dependencies. For some classes this was pretty straight forward when there were a surrogate key present. The table relationships were modeled as bags and many-to-one dependencies.
But when it came to the composite-ids, I run into problems. In one special case, I couldn't find a good solution at all. Because I want to avoid classes for the mapping tables, I had to join them in the hibernate mappings using the <join> statement. Hibernate seems to allow joins between two tables only, if they have the same set of primary keys. But in my case one of the tables where using composite-ids, while the other where using just a single foreign key. The result was this error message:
The only solution I found here was to join the tables inside the database using views. But this requires that these views are updateable, which is not always possible.
org.hibernate.MappingException: Foreign key (FKF92C56FC70FE07E3:form [form_id])) must have same number of columns as the referenced primary key (form_data [form_id,form_data_id])
So after my first experiences I see a mixed picture of my new Hibernate based storage layer. While there are some classes that can be handled by Hibernate very easily and I can profit from the powerful IDE support, there are others where I not only had a hard time to implement the mapping. Here complexity of updates and queries is the same as before and some are even more complex. The native queries generated by Hibernate sometimes are inefficient and hard to debug if they do not work as expected. In some cases Hibernate generates a huge amount of queries, where I had just one before. Perhaps there are ways to avoid this by tuning, but then again, where is the advantage of Hibernate over SQL?
Surely it is too early to give a final statement about the pros and cons of migrating an existing project to Hibernate, since there are still a lot of open tasks to do. But I can say already, that switching to Hibernate is a noticeable amount of work and even a serious risk. And I would strongly recommend everybody to first evaluate if the advantages of a migration are worth taking it.
Trackback specific URI for this entry