Browse DevX
Sign up for e-mail newsletters from DevX


Object Generation: A Better Approach to Hibernate Integration : Page 2

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.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

The AbstractGenericEntity Class

The AbstractGenericEntity class is the base class for all persistent entities. Its purpose is to contain the surrogate key (the ID property) and correctly implement the hashCode and equals methods.

Listing 4 is the full source for the AbstractGenericEntity class. You'll notice that the implementations of the hashCode and equals methods are simple and efficient. The class has no string comparisons, and the methods are marked final to prevent business objects from overriding them:

@Override public final int hashCode() throws IllegalStateException { ... return id.hashCode(); } @Override public final boolean equals(Object that) throws IllegalStateException { if (this == that) return true; if (!(getEntityClass().isInstance(that))) return false; ... return id.equals(((AbstractGenericEntity) that).getId()); } public abstract Class<E> getEntityClass();

The abstract getEntityClass method is used by the equals method and must be defined in your concrete business object. You saw an example of this in the previous code for the User class.

With the business object defined, it's now time to create its companion DAO. Once again, the code you'll write is very simple, with all the heavy lifting handled in an abstract base class.

The Data Access Object

Every business object will have a corresponding data access object that it uses to manage the standard CRUD (create, read, update, and delete) operations. This is a familiar design pattern commonly used in Hibernate applications. Thanks to the use of Java's new generics capability, the code for the User DAO is remarkably succinct:

public class UserDao extends AbstractGenericDao<User> { public User findByEmail(String email) { List<User> users = find("from User u where u.email = ?", email); return ((users.isEmpty()) ? null : users.get(0)); } @Override public final Class<User> getEntityClass() { return User.class; } }

The findByEmail method is a simple query method for finding users. Without it, this class could've been reduced to a single line of code.

The AbstractGenericDao Class

The AbstractGenericDao class is the base for all data access objects. Its purpose is to provide CRUD capability and to correctly implement object generation. Remember, object generation is just object creation coupled with the assignment of an identifier.

Listing 5 is the full source for the AbstractGenericDao class. It's not trivial, but it's manageable if you take it in chunks. One inner class, EntityGenerator, encapsulates all the important behavior. New entities are generated in its generateEntity method:

public E generateEntity(SessionImpl session) throws InstantiationException { ... entity = (E) generatedEntityClass.newInstance(); entityPersister.setIdentifier(entity, generateIdentifier(session), session.getEntityMode()); ... return entity; }

The calls to newInstance and setIdentifier are self-explanatory, but more is going on here than meets the eye. The new instance that's created is not an instance of the original entity class. Rather, it's a runtime-generated subclass that's been enhanced to add additional behavior.

The generated subclass is a simple extension that implements the GeneratedEntity interface. You won't find this interface in the Hibernate framework. Rather, it's a simple, custom interface that's used to mark objects whose identifiers have been assigned during creation, otherwise known as generated entities:

public interface GeneratedEntity { public boolean isTransient(); public void setTransient(boolean unsaved); }

The transient property is used to track an object's persistent status. It's an important detail that'll show up later when discussing the Hibernate interceptor.

The enhanced subclass itself is created in the createGeneratedEntityClass method. It's this method that makes use of the Javassist library:

final CtClass ctGeneratedClass = classPool.makeClass(generatedEntityClassName, ctEntityClass); final CtField ctUnsavedField = new CtField(CtClass.booleanType, "unsaved", ctGeneratedClass); ... ctGeneratedClass.addInterface(ctGeneratedEntityInterface); ctUnsavedField.setModifiers(Modifier.PRIVATE); ctGeneratedClass.addField(ctUnsavedField, CtField.Initializer.byExpr("true")); ctGeneratedClass.addMethod(ctIsTransientMethod); ctGeneratedClass.addMethod(ctSetTransientMethod); return ctGeneratedClass.toClass(entityClass.getClassLoader()); ...

The result is a Java class that, if implemented in Java code, would look like the following:

public class GeneratedUser extends User implements GeneratedEntity { private boolean unsaved = true; public boolean isTransient() { return unsaved; } public void setTransient(boolean u) { unsaved = u; } }

The enhanced class is created just once for any given entity type. Subsequent requests retrieve a cached instance.

You may have noticed that the code uses a few Hibernate implementation classes that aren't typically exposed. For example, it's necessary to reference the SessionImpl and AbstractEntityPersister classes to access Hibernate's underlying functionality. The solution simply won't work without them. Purists may disagree, but this isolated, minor deviation is acceptable given the solution's practical benefits.

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