Tech Blog

Dr. Mindbender, Or How I Learned To Stop Worrying And Love Hibernate

Posted by Steve Ayers

Jun 7, 2011 2:51:08 AM

I’ll admit I was wrong.

For years, I have been beating the drum about town, proselytizing my distaste for Hibernate. I told everyone that would listen what my opinions were. ‘It only gets in the way’, I said. ‘Its too much of a black box’.

And while some of those notions still hold true, I am here today to admit I was wrong in that Hibernate is not a total obstacle of a framework. In the right situations and circumstances, it actually is very nice to have on your side. In the wrong situations, however, it is a nightmare. It is not something that can be forcefed into every situation and while it has its benefits, using it in the wrong project will provide nothing but heartache. Trust me, I know from experience. It is the Dr. Mindbender of frameworks: use it where it should be and it’s a peace-loving orthodontist. Misuse it and you have a hateful, deceitful villain with a proclivity for cybernetics and brain-scrambling.

I spent a decent amount of time on a project that flouted tried-and-true Hibernate conventions. Admittedly, this was my first proper working experience on a major project with it, so naturally, I became prejudiced against it. ‘THIS is what all the Hibernate fuss is about?’, I said, the platform for my future drum-beating beginning to take hold. ‘I know how to write my own database queries, no thanks’.
So from then on, I became a zealot, proudly jutting out my chest in interviews and on projects about how it was worthless, how I’d rather just use easier approaches like Spring and JDBC.

Then, something unexpected happened.

I worked on a project that actually made sensible use of Hibernate. I got to see how a proper persistence-mapping file was created. I saw first-hand some of the benefits of Hibernate that I didn’t even know existed; benefits that weren’t in my egotistical arsenal of query-writing and transactional coding.

But, old habits die hard. In technology, you learn as you go, so it makes no sense to completely flip-flop from one side to another. To be a good developer (to go from journeyman to master, if you will), you should be an amalgam of your experiences. Draw from what you learned and make informed decisions.

So, to prove my point, I will outline the good and the bad of Hibernate. I will tell you when to use it, when not to, and some practices to avoid. And hopefully, you’ll recognize when you should make use of it and when you should avoid it like the plague. After all, there’s a time and place for everything.

The Good

No Query Writing

Probably the biggest and highly-touted feature of Hibernate is that it abstracts the database layer away from the developer. No more fiddling around with DB queries and getting your hands unnecessarily dirty with database connections. Through simple methods such as Session.load() and Session.get(), retrieving an object is simple. In addition, Hibernate’s Criteria API makes complex joins relatively simple and easy to create.

For example, suppose your system has an Order object which has a many-to-one association to a Customer object and the association is by Customer ID. Further suppose that the query to retrieve both orders and customers pits up against two brutally large and inefficiently constructed tables. If you have not specified a fetch strategy on the relationship, the default behavior for Hibernate is SELECT, which means to retrieve an order, two queries will be run: one for the Order and one to populate the Customer association. Normally, what you’d do in plain JDBC world is to simply join these two queries together on the Customer ID, which is a process that can be a pain, especially if the queries are large, the join columns are complex, or the queries are stored in code (requiring a recompile).
However, in Hibernate, you simply change the fetch strategy to JOIN in the mapping file and voila, Hibernate joins the queries together and retrieving an Order runs one query, bringing back Order details and Customer details in one shot.

Caching

Caching is an item that could be its own blog post (I smell sequel!), but I will touch on it here. There are three areas of caching involved with Hibernate: 1st-level, or session-caching, 2nd-level caching, and query caching.

In a nutshell, session caching involves the ability of Hibernate to store objects in cache scoped to the current Hibernate session. What this means in layman’s terms is that the following code:

  Session.load(MyObject.class, 123);
  // Other logic here
  Session.load(MyObject.class, 123);

will only invoke one query for MyObject. The initial invocation hit the database and stored the information in Hibernate’s session cache. Since we are still in the same session, another request for that object will simply retrieve it from the session cache and not pull it from the database.

The second-level cache and query cache work together and are scoped across Hibernate sessions. The 2nd level-cache stores IDs of objects keyed to the respective object values (not the objects themselves). The query cache stores WHERE clauses and their bound parameters keyed to object IDs. Used together they represent a powerful way to improve performance by storing oft-requested information in a cache and minimizing the hits on the database over time. If your application is easily cacheable, then Hibernate could be a good choice for you. It can really speed up response time by eliminating all that expensive database interaction.

So, how do you know if your application has good cache candidates? Well, stay tuned for the sequel. You didn’t learn the back story of Don Corleone in Godfather I did you?

DB From Scratch

One such area that begs for Hibernate is when the database is being designed from scratch. This is not an existing database you are trying to shoehorn Hibernate in front of, this is one that can be designed with an ORM solution in mind. It is one that can be designed for abstractions.

As a result, you can take a more object-oriented approach to fashioning the database and its relationships. You can assure that tables are created that are easily modeled to objects and that the relationships that are defined are properly mapped. If you think in terms of the system and how Hibernate functions, designing the database will be easy and as a result, Hibernate will be a seamless integration into your system.

Lower DB Learning Curve

From a consultant’s point-of-view, it makes it far easier to learn the database and its relationships when a proper and well-documented implementation of Hibernate is in place. Learning database tables and their structures is simple just by scanning the persistence-mapping file. No need looking at cryptic table names and columns where you have to first parse the English translation of them before you can even think of how they all relate together.

Further, the Hibernate entity objects make a developer-friendly way to look at the business model. Everything is laid out into handy Java objects, enabling you to take a more code-centered approach towards learning the domain. In addition, learning data types of fields is extremely easy and all of this makes onboarding that much faster. Which would you rather spend your time learning:

ORD_MASTER

ORD_ID
ORD_NM
CST_ID
QTY
TTL
STS

Or this:

public class Order {
    	private String orderId;

    	private String orderName;

    	private Long customerId;

    	private Integer quantity;

    	private Integer total;

    	private String status;
}

So, while this may not be necessarily a reason to implement Hibernate, it is one positive aspect of it. As a consultant, anything that enables you to learn the client domain faster is a good thing.

The Bad

Query Writing

OK, so maybe I was wrong when I said there’s NO query writing. Sometimes there is. Hibernate has its own query language called the (wait for it) Hibernate Query Language, or HQL. In addition, its syntax is not completely identical to SQL, which I’m guessing is the database language in which you spent a few years dabbling.

Well, they say to learn to new a language once a year, so here’s your newest one. Luckily, you shouldn’t need to use this that often, especially if you have a well-crafted mapping file and make efficient use of the Criteria API. Any ORM worth its salt and that prides itself on database layer abstraction should not force the developer to rely on query-writing, even if it is relatively object-oriented.

Unwieldy Legacy Databases

Legacy databases pose a problem for Hibernate. There are many aspects of a database that can creep in over time and most of them do not react well when an ORM pokes its nose in on their business.

One such sign that Hibernate is a bad idea is business logic in the database, such as in PL/SQL packages and procedures. This type of logic works behind Hibernate’s back and provides nothing but headaches to attached entity objects in your code. For example:

MyObject is retrieved from the database. While it is hydrated, a procedure is invoked in the database that updates a few tables, including the table to which MyObject is connected. Now, your object has stale data inside that it thinks is valid. You now have to flush your object and get the up-to-date data. This kind of thing can be difficult to fix and even more difficult to diagnose.

In addition, with legacy databases, often times developers tend to know the schema and inner workings extremely well. They become intimately familiar with the poor performing tables, where the indices are, and how the relationships work. As such, sticking Hibernate in front of them makes for an extremely problematic transition. They will see Hibernate as a black box obstacle that they have to defer to regarding the database. When you have a group of developers who are used to getting their hands dirty with the data, sticking an ORM framework in their way is going to cause problems.

Unnecessary Information

This item was one of my talking points during my Great Anti-Hibernate Crusade. I’ve grown less vitriolic with this concern in my old age, but its still something I have a bit of trouble with and have never really received a complete and acceptable answer.

Modeling data to objects using an ORM like Hibernate simply returns more data than needed. Period. There are going to be times when you need the values of five columns from a fifty-column table. In addition, this table could have a plethora of one-to-many associations which are fetched eagerly. So, for your five columns you are returning back 47+ values you don’t need and running who knows how many queries needlessly. Sure, you could turn off the eager-loading, but that affects any other logic that needs these associations at the time they’re loaded.

The explanations or resolutions for this I’ve been proposed over the years are that you should design your system in a way where model objects can be used throughout all layers. In a perfect world, that seems reasonable. But user demands are not always reasonable. So, what happens when they request to see a popup on screen showing only those five values from that fifty-column behemoth? Should I tell them I can’t because it doesn’t work well with model objects?

Designing with a model object approach in mind is a great idea with new development, but applying this to legacy code is a headache.

The way I see it, you have three options in the above scenario:

1. Just retrieve the object as its modeled and send the mounds of information back even if its not needed
2. Use a Data Transfer Object
3. Consider a refactoring of the modeling / relationships.

All have tradeoffs and none seem like a silver bullet approach. The last object is probably not going to happen. This is especially true if this is a legacy system with rotting design.

The first option is most likely, but as I said will result in needless data and/or queries being run, but admittedly this is something that could be mitigated with caching strategies.

The second option is sort of like throwing in the towel. Its like admitting you were beaten by requirements. The second you let DTOs creep into the mix, things start to get infected and you’re losing the very foundation on which you built your Hibernate architecture in the first place.

So, that is what I’ve learned so far in my love-hate relationship with Hibernate. I suspect many developers have a love-hate relationship with a lot of tools and mine just happens to be this one. I think it benefits a developer to see the good and bad of each and not just develop unfounded opinions based on one bad experience. That way, they can make an informed decision in any circumstance. Learning to do this allows you to have an open-mind and grow in your knowledge, eventually becoming an evil genius capable of mind control and dentistry, like Dr. Mindbender.

Topics: Agile and Development

Subscribe to Email Updates