Using Hibernate Frameworks: What Are the Best Practices?

I’ve recently been involved in helping CAST Research Labs analyze a large sample of Java EE applications (496 apps), looking to understand the impact of different frameworks on application structural quality. We analyzed these applications using CAST’s Application Intelligence Platform (AIP) to identify critical violations of architectural and coding practices. While looking at the critical violations that were detected by CAST, something struck me: The success ratio (i.e. the ratio between the number of times a rule is violated and the number of opportunities this rule could have been violated) for rules associated to Hibernate was particularly low, indicating issues related to performance and robustness for applications using this framework. (The details of the report will be published next week – we presented a preview of the analysis during a webinar in January.)
Hibernate is one of the most popular frameworks in the Object Relational Mapping area. It prevents you from dealing with the complex task of mapping objects to relational database allowing the development your data layer using only POJO, and keeping your application portable through existing databases. But at the same time, Hibernate solves any existing mapping issues, making it difficult to implement under correct performance and robustness standards.
In my previous post, I discussed whether frameworks could simplify our lives. In this post, I want to focus on Hibernate and which best practices you should follow when using it in your Java EE application.
The rules associated to Hibernate that had the lowest success ratios were the following.
Persistent classes should Implement hashCode() and equals()
In our analysis, this rule had the lowest success ratio (7.70 percent) across all frameworks analyzed, affirming that this architectural practice is too often ignored by developers. Although Hibernate guarantees that there is a unique instance for each row of the database in a session, you still need to supply your own implementation of the equals() and hashCode() methods for your persistent classes whenever you work with objects in a detached state. This is particularly true when you test these objects for equality, usually in hash-based collections.
Avoid using references to the ID in the persistent class’ method equals()
In our analysis, this rule had the second lowest success ratio (37.13 percent). It is possible for the programmer to define the meaning of Java Equality. However, Hibernate will only set the ID field when saving the object; it is therefore important not to use the ID field in the Java Equality definition when it is a surrogate key. For that reason, saving the object that has been added to a set collection results in identity change. In addition, the behavior of the Set/Map collection class is not specified when the value of an object is changed in a manner that impacts equals comparisons while the object is an element in the Set or is the key of a Map, you might corrupt your database.
For example:
Person p = new Person();
Set set = new HashSet();
set.add(p);
System.out.println(set.contains(p));
p.setId(new Long(5));
System.out.println(set.contains(p));
Prints: false
Other best practices that should be followed when using Hibernate with your Java EE applications are the following:
Never use array to map a collection
The details of an array mapping are virtually identical to those of a list. However, we strongly recommend against the use of arrays, since arrays can’t be lazily initialized (there is no way to proxy an array at the virtual machine level). Lists, maps, and sets are the most efficient collection types.
So, using array can affect your application performance when it contains many items: lazy loading, optimized dirty checking, and poor performance features for persistent collections.
Avoid public/protected setter for the generated identifier field
A primary key value must never change once it has been assigned. Since it is a generated key, it is automatically set by Hibernate, or by another JPA implementation or by another provider. The actual behavior of an application tries to modify the value of a primary key that is not defined.
Avoid many-to-many association
“Many to many” usage is discouraged when a simple bidirectional “many-to-one”/“one-to-many” will do the job. In particular, a many-to-many association might always be represented as two many-to-one associations to an intervening class. This model is usually easy to extend. In a real system, you might not have a many-to-many association as there is almost always other information that must be attached to each link between associated instances, such as the date and time when an item was added to a category. The best way to represent this information is via an intermediate association class. On top of this, changing the definition of a primary key and all foreign keys that refer to it is a frustrating task.
Persistent class method’s equals() and hashCode() must access its fields through getter methods
This rule is important: the object instance that is passed as ‘other’ might actually be a proxy object and not the actual instance that holds the persistent state. This is the case where there are lazy associations between classes. This is one area where Hibernate is not completely transparent. But it is good practice to use accessor methods instead of direct instance variable access. When we are tuning the performance of the application, a lazy association might actually be required.
This potential issue raises a ClassCastException and can cause the application to become unstable.
Avoid non serializable Entities
When Entity bean instance is to be passed by value as a detached object (for example, through a remote interface), the entity class must implement the Serializable interface.
Also, in some cases an OptimisticLockException will be thrown and wrapped by another exception, such as a RemoteException, when VM boundaries are crossed. Entities that might be referenced in wrapped exceptions should be Serializable so that marshaling will not fail. One of the consequences to not following this rule is receiving an exception when a non Serializable Entity is passed by value.
This is just an extract of best practices on Hibernate, but you can already see that not following them can have severe consequences in terms of robustness and performance. These rules can be quite obvious by an expert of Hibernate, but for the novice user, Hibernate can be tough to use. Abstract is big and complex and the user must spend more time in assessing the concept, function, and uses in the developing program.

Do software frameworks simplify your life?

We’re covering Java frameworks and their impact on application quality in an upcoming webinar, Java Applications and Coffee: The Variations are Endless, on Jan 29. As part of that, I wanted to share some insights along the lines of what we might discuss during the webinar. But first, what is a software framework?
A software framework is an abstraction in which software provides generic functionality. It is universal and can be reused by different applications.
Nowadays, it seems impossible to start the development of an application without thinking about frameworks. Some of them appear as a de-facto standards used in most applications, such as log4j with its implementation in other languages.
For other frameworks, it can be more difficult to choose. For example if we look at ORM frameworks in J2EE technology, you can see that there are more and more new frameworks.

And there are many more presentation frameworks.
The problem with this list of frameworks — that are supposed to simplify the coding of an application — is that you must master the framework itself in addition to java.  And it’s not as easy as it seems. Take, for example, all the books written just for the Hibernate framework:

When analyzing several applications that use hibernate, I often found that persistent classes do not implement hashCode() and/or equals() for example
You have to override the equals() and hashCode() methods if you:

intend to put instances of persistent classes in a Set (the recommended way to represent many valued associations); and
intend to use reattachment of detached instances.

What does that mean? It means that Hibernate guarantees if there is a unique instance for each row of the database inside a session. But whenever you work with objects in detached state, and especially if you test them for equality (usually in hash-based collections), you need to supply your own implementation of the equals() and hashCode() methods for your persistent classes.
Nevertheless, it’s possible to build a complex application with identity (default) equals as long as you exercise discipline when dealing with detached objects from different sessions. If this concept of equality isn’t what you want, you must override equals() in your persistent classes. But this method requires discipline and it’s easy to make a mistake.
Thanks to CAST’s solution, you can check that this rule is enforced but many other one related to robustness, security and performances.
CAST’s solution considers J2EE application not as a single Java[/JSP] application, but provides rules for the most common frameworks such as Struts 1 & 2, Tiles, JSF, Spring, Hibernate, JPA compatible frameworks, and EJB. CAST’s product takes into account java annotations, XML files, and of course Java language to check these rules.
In addition, it can be extended to manage other frameworks.
As we have seen, implementing a framework is not so easy
It is obvious that frameworks simplify the development; it saves you from reinventing the wheel. And they usually come with a community built in. The bigger the community, the better the framework will be in terms of stability and completeness.
But as we’ve seen, frameworks come with their own rules that must be followed to avoid mistakes that can come up later in the development lifecycle and are difficult to diagnose. This is why it is important to have a static analyzer check that the frameworks are following best practices.
Again, if you’re interested in learning more about the resiliency of Java frameworks, be sure to check out our most recent CRASH report, which compared the quality and stability of Java frameworks for enterprise applications. Keep in mind, this is the only available repository in the world of real business software that has been subjected to this level of scrutiny. And for a deeper dive into the research results, be sure to register for our Jan. 29 webinar, Java Applications and Coffee: The Variations are Endless, which covers the full findings of the research.