RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Object Generation: A Better Approach to Hibernate Integration

If you're using Hibernate, your business objects may be littered with unnecessary code. Learn a different, proven approach that challenges conventional wisdom and promises more streamlined integration with less code.

ibernate is a practical, high-performance Java persistence framework. Using Hibernate is nearly transparent, but the one detail that even Hibernate can't hide is the notion of object identity and equality. It's a critical detail that typically infects all persistent objects with tedious and error-prone code. This article offers a different, proven approach for working with Hibernate that eliminates the clutter, and is both simple to incorporate and appropriate for most modern enterprise applications.

What You Need
Java 2 SDK
Hibernate 3.1
Javassist 3.1

Hibernate Persistence: The Classic Approach

Hibernate is designed to easily enable the persistence of virtually any plain old Java object (POJO). Creating a persistent object is simple, and the classic approach that the Hibernate community recommends can be summarized in just two steps:
  1. Select a business key and implement the hashCode and equals methods on the object. A business key is some combination of immutable business fields that's not only unique, but guaranteed not to change over the lifetime of the object. It's used to identify objects and test for equality.
  2. Write a Hibernate mapping descriptor. The descriptor maps fields in the object to columns and tables in the database. A descriptor can take various forms, but it's commonly provided as an XML file.

Listings 1, 2, and 3 are simple examples that demonstrate this process:

  • Listing 1 is the Java code for a simple User class that's been enabled for persistence. In this example, the user's email address has been selected as the object's business key. Notice how it's used in the implementation of the hashCode and equals methods.
  • Listing 2 is the corresponding Hibernate descriptor. It simply maps fields in the object directly to columns in the database.
  • Listing 3 is the schema for the underlying database table. Notice that it's the ID, and not the email address, that serves as the primary key. This is known as a surrogate key, a common best practice in database design.

The classic approach works, but it's got some notable shortcomings. For the following reasons, the first step (selecting a business key and implementing the hashCode and equals methods) causes trouble for many Java developers:

  1. Many, if not most, Java objects don't have convenient business keys. This is clear even in the sample User class. The email address is used as a business key, but it's not a good fit since users should be able to easily change it. In fact, it's common to have objects where nearly all fields can be changed. This often requires workarounds to make these objects fit the model. You'll see these workarounds both in the objects themselves and in the code used to manage them.
  2. Implementing the hashCode and equals methods throughout a project's persistent objects is tedious and error-prone. It's just more extraneous code that you need to be write, test, and maintain.
  3. Business keys are often composed of a combination of strings. Remember, the hashCode and equals methods are used frequently behind the scenes, and although performance may not be an issue, it can be if these strings are long and similar. Besides, you're probably always looking for easy optimizations.

Fortunately, these issues can be addressed with a new approach to Hibernate integration.

Hibernate Persistence: The Generation Approach

Object generation is a different approach to Hibernate integration. Instead of business keys, objects are identified by their surrogate key, without the need to provide custom hashCode and equals methods for each object. The concept is simple, but implementing it requires a bit of understanding and infrastructure.

A surrogate key is a unique identifier generated for each row in a given table. If you looked at the previous listings, you'll see it as the ID column on the user table along with its companion sequence used to generate values. This means that every User object that's persisted will have its own unique integer identifier.

A surrogate key seems like a natural choice for an object identifier, and it is but for one small problem: it doesn't normally get its value until the object is persisted to the database. This means that an object that's added to a collection before it's persisted will malfunction (collections call the hashCode and equals methods behind the scenes).

The solution is to use an object generator, a modified data access object (DAO) that retrieves and assigns identifiers during object creation. Think of it as an object factory designed for Hibernate. Each object returned from the generator has a database-generated identifier that's guaranteed to be stable and unique. The key difference between this and the classic approach is that this all happens before the object is persisted.

Making this magic happen and seamlessly integrating with Hibernate requires a bit of infrastructure. Each of the following elements is relatively straightforward, but they all need to be well orchestrated:

  1. An abstract entity base class. This class contains the implementations of the hashCode and equals methods used for all persistent objects. Strictly speaking, this class isn't necessary, but it does eliminate extraneous code.
  2. A modified DAO. This is the object generator that retrieves and assigns identifiers upon object creation. This class makes use of the Javassist library mentioned in the requirements for this article. If you've never used Javassist, it's an excellent open source library for creating or enhancing classes at runtime.
  3. A simple generated entity interface. This is an interface that all generated entities implement at runtime (via the Javassist library). It serves as a marker interface with a few simple methods to track an object's persistent status.
  4. A custom identifier generator. This is an implementation of a Hibernate IdentifierGenerator that understands object generation and delegates to an underlying identifier generator. This is easier to demonstrate than it is to explain.
  5. A custom Hibernate interceptor. The interceptor optimizes performance by using the generated entity interface to minimize database calls.

Luckily, you'll see none of this complexity in your final business objects, and that's the point. The task of adding persistence is reduced to extending a base class and creating a minimal companion DAO. For the basic User class found in the previous listings, its code is reduced to little more than the necessary business logic:

public class User extends AbstractGenericEntity<Long, User>
   private String email;
   private String password;

   public String getEmail() { return email; }
   public void setEmail(String e) { email = e; }
   public String getPassword () { return password; }
   public void setPassword(String p) { password = p; }

  public final Class<User> getEntityClass() { return User.class; }

You'll notice that this class doesn't include a hashCode or equals method. Nor does it include an ID property. They're all contained in the AbstractGenericEntity class along with the declaration of the getEntityClass method.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date