A Simple Data Access Layer using Hibernate

by
Mario Aquino, Software Engineer
Object Computing, Inc. (OCI)

Introduction

There are a variety of open source tools available today for constructing a data access API, which simplify what has
been in the past a complicated and error prone mechanism. Before these tools became available, applications resorted
to calling JDBC APIs and passing SQL strings to Statement objects to execute data lookup queries. The
lookup calls returned ResultSets that an application would use by calling accessor methods matching the
data types of the returned columns. While effective, this approach is fragile because it relies on
Strings in application code matching the names of database tables and columns; changing the names of
database tables or columns required finding all of their references in the code and changing them. This is
problematic if not inelegant.

A better way to interact with a datastore is to eliminate explicit hand-written references to datastore entities
while also providing a simple and intuitive API to retrieve and update data that is backed by a database.
Preferably, the alternative should not rely on JDBC Statements, SQL strings, or ResultSets.
Instead, a solution could create a natural mapping between Java objects and database entities, one that would
require a minimum of hand coding to maintain that mapping without removing any of the data retrieval and control
facilities provided in the JDBC APIs. This article will introduce two tools that radically simplify the data access
development process as well as a lightweight framework built on top of these tools to hide their implementation
details from a client application that wishes to leverage the benefits these tools provide.

Hibernate

Hibernate is an open-source object/relational mapping toolkit that relieves the need to make direct
use of the JDBC API. Hibernate offers facilities for data retrieval and update, transaction management, database
connection pooling, programmatic as well as declarative queries, and declarative entity relationship management.
Hibernate also has the ability to generate Java source files to match the structure of a database, as will be
discussed in more detail in the next section.

XML files containing configuration data provide Hibernate with details about databases with which it needs to
interact. These files contain database connection specifics, connection pooling details, transaction factory
settings, as well as references to other XML files that describe tables in the database. Combined, these files
provide substantial configurability allowing an application to tune the behaviors and performance of its data access
layer to a remarkably fine level of granularity.

Code Generators

One of the keys to a flexible architecture is the ability to leverage code generation tools for as much of the data
access layer as possible. Hibernate comes with a code generation component that produces Java source files based on
object-relational mapping expressed in its configuration XML files. These files map database table columns to Java
class fields, matching equivalent datatypes, identifying primary key fields, and specifying relationships
(one-to-one, one-to-many, many-to-one, etc.) between entities. Below is an example of a configuration XML file for
an "Order" entity:

This sample XML file tells Hibernate to use a class called "com.ociweb.Order", which should have three primitive
fields: a long field called "id", a boolean field called "paymentconfirmed", and a short
field called "installments". Additionally, the class will have three fields that represent relationships between the
Order and three other entities: the "customer" field (that relates to a Customer object
with which it has a many-to-one relationship), an "orderitems" field (that relates to a set of
Orderitem objects, here a one-to-many relationship), and a "delivery" field (which points to a Delivery
object representing a one-to-one relationship). Hibernate does all the work to maintain relationships between tables
in your database, using details specified in the configuration XML files. The XML above also has instructions for
Hibernate to load relationship references between tables lazily (the lazy attribute), to treat one of
the relationships as bi-directional (the inverse attribute), and to do cascading deletes on child
records when their parent records are removed (the cascade attribute). As well, Hibernate can make use
of outer join fetching (allowing retrieval of objects with many-to-one or one-to-one relationships as one object) to
reduce the number of roundtrips to and from the database. It is configurability features such as these that make
Hibernate very powerful.

Another open-source tool called Middlegen already has the ability to connect to a database server
and examine the database metadata to discover table definitions and relationships. As well, Middlegen comes with a
plugin for Hibernate that allows it to generate the Hibernate configuration files automatically. The Middlegen and
Hibernate code generation utilities can be run from Ant targets they each supply.

Utilizing code generating tools for a large portion of the data access layer means that the structure and
organization of an application's data model can evolve and that the classes that mirror that structure can be
regenerated consistently and immediately. Unfortunately, a changing data model does mean that portions of an
application that use the data model's API must also be updated to reflect the new changes. This can be managed
relatively painlessly either with modern refactoring tools (which are available in several popular IDEs) or through
the use of tools to autogenerate other application areas like the View Layer, which would likely be impacted by
changes in the Model.

Now that the code generation and data management tools have been introduced, the rest of the article will focus on
the construction of a light-weight data access framework that will separate data clients from the tools that provide
the persistence layer.

A Service Layer

It is a good idea to construct a layer to separate the rest of an application from Hibernate so that unnecessary
dependencies are not created by our data access toolkit. This layer should be responsible for interfacing with the
Hibernate data retrieval APIs and managing transactional boundaries. We should be able to identify an interface
pattern to manage access to our domain objects: all will need CRUD (Create, Read, Update, and Delete) methods, a
method to find a domain object by its primary key, and a method to find all instances of a domain object. This
pattern should hold true for most if not all domain objects that map to entities in the application database. The
interface definition below follows this pattern:

This interface is used by clients to lookup instances of any "DomainObject" (i.e. an interface exists for each
domain class). With similar interfaces declared to manage all of the domain objects of an application, the next
component we need is a Service Locator to return references to the implementation of these interfaces. To simplify
things, we can define the Service Locator interface with a single method that accepts a Class reference
for the domain object manager interface that the client needs. The interface definition below demonstrates this:

The code above is a simple and straight-forward example of the use of this service layer. The client code does not
need to know about the implementations of the ServiceLocator or the domain object manager (OrderManager
above). Frameworks that make extensive use of interfaces tend to promote ease of testing and the ability to do
parallel development. As long as the contracts that an interface provides are well understood, the implementations
of interfaces can be mocked-out by their clients until those implementations are completed. Thus the client and the
interface implementation are decoupled and can be developed independently. A previous Java News Brief titled
"Designing Testability with Mock Objects" focuses on this topic (see the Resources section below for a link).

Each of the operations of the domain object manager's interface can be broken down into independent commands that
could be applied generically to all domain objects. Hence an "AddCommand" would be defined to add new domain object
records to the data store, the same would be true for "UpdateCommand" and "DeleteCommand". The finder methods can
also be made generic. Each appropriate command would be invoked by the implementation class of the domain manager
interface. However, rather than create a separate class to implement each domain manager interface, a dynamic proxy
can be used to wrap the domain manager interface and invoke the appropriate commands since they all follow the same
pattern. The diagram below shows the layout for the interfaces and implementation classes that make up the core of
this simple framework.

Figure 1

The Command interface below takes a reference to the method of the domain object manager interface that
is being invoked, along with any parameters passed in the invocation, and a reference to a Hibernate
Session object. The Hibernate Session interface conveniently provides all of the
persistence and query methods that the commands need.

The ServiceLocatorImpl is mainly responsible for returning a reference to a domain manager given its
interface class. All of the interfaces will be implemented by a dynamic proxy that will delegate method invocations
to a corresponding command object. So it seems there are three responsibilities that need to be accommodated:
providing a means to access each supported command object, validating that the Class objects passed
into the getManager() method have methods that follow the domain object manager interface pattern, and
creating proxies that will respond to method invocations by delegating to the appropriate commands. The code below
demonstrates this.

The "validity" checking methods above (validateIsInterface() and
validateHasDomainObjectMgrAPI()) only really test that the passed in Class object is an
interface that contains all of the methods described in the domain object manager pattern. The signatures of those
methods are not also checked, which could be an area for improvement. For simplicity's sake in this article, the
name check is good enough.

Another noteworthy aspect of the implementation above appears in the getSession() method of the ManagerDelegate
inner class, where a call is made to a ThreadSessionHolder to retrieve a reference to a Hibernate
Session object. As its name implies, the ThreadSessionHolder associates a reference to a
Hibernate Session to the currently executing thread. While the Hibernate Session is used
to retrieve and update objects that are mapped to database table rows, it also acts as a cache for those persistent
objects keeping references to objects it has retrieved. By associating the Session with the current
executing thread, objects retrieved through different domain object managers will be able to establish and remove
relationships between themselves without having to explicitly share the same Session; since the Session
is tied to the thread that all domain object managers are running on, Session sharing happens
transparently. A consequence of this design, however, is that the scope of the Session needs to be
managed somehow. In a J2EE application, Session scoping can happen at the request level so that each
domain object manager invoked during a single request shares a common Session that is closed at the
return of a response. For a non-J2EE application, a similar model is possible though it requires a component with
the responsibility of defining a "request".

FindWithNamedQuery

The resolveCommand() method in the ManagerDelegate above tries to retrieve a
Command object whose name matches that of the invoked Method. If it doesn't find one, it
tests whether the name of the invoked Method begins with 'find', and if so returns a command that
hasn't yet been discussed, the FindWithNamedQuery command. This command utilizes Hibernate's named query capability, whose mechanics will be discussed in a later
section. The command adds support for arbitrary finder queries which may appear in the domain object manager
interface. The only requirements for these queries is that their methods begin with "find" and return a java.util.Collection
reference and that a named query matching the method name appear in the XML configuration file of the domain object
whose manager interface contains the finder method. For example, a finder method could be added to the DomainObjectMgr
interface above that found instances of DomainObject by name:

The query defined in the mapping file must be named according to the fully qualified class name of the domain
manager interface plus the name of the finder method itself ("com.ociweb.domain.DomainObjectMgr.findDomainObjectByName"). Additionally, this query takes a parameter that the finder method in the DomainObjectMgr
interface defines as a String. The order and types of parameters declared in the finder method
signature must follow the parameters expected by the query definition.

Transactions

As Figure 1 above shows, three of the commands extend from a TransactionalCommand base
class. The purpose of this class is to wrap the execution of a command within a transaction. This class follows the
Gang of Four Template Method pattern, which starts a transaction at the invocation of its execute()
method then calls an abstract method (command()) which must be implemented by its child classes. The
TransactionalCommand class appears below.

The other two transactional commands (UpdateCommand and RemoveCommand) follow the exact
same simple style of the AddCommand.

Flexible Querying

Along with a simple API for retrieving and persisting objects mapped to relational tables, Hibernate also provides a
very robust querying API that supports query strings, named queries, and queries built as aggregate expressions. The
querying API is exposed through two interfaces, the Query interface and the Criteria, with
the former supporting queries passed in as Strings in "Hibernate Query Language" (HQL) syntax
(or through the invocation of predefined "named" queries, which will be examined later in this section) and the
latter designed for aggregate queries.

Query

The Hibernate Session interface acts as a factory for Query objects, which can be created
either by passing a String in HQL syntax (which is similar to SQL, though object-oriented and able to
understand inheritance and polymorphism) to the createQuery() method or by calling the getNamedQuery()
method and passing it the name of the query that has been defined in one of the configuration XML files for
Hibernate. Hibernate Query objects can perform lookups using static values (e.g. "from
com.ociweb.Customer as customer where customer.name='Als Petstore'") as well as parameterized queries (e.g.
"from com.ociweb.Customer as customer where customer.name=?"). In fact, parameterized queries can be
stated in two forms, one where the positional question marks ("?") can be replaced with parameter values assigned
according to the ordering of the question marks in the query string, or another where named parameters are used
instead of the positional questions marks. A "named parameter" query string might look like this: "from
com.ociweb.Customer as customer where customer.name=:name". The :name parameter would be
replaced in a call where the parameter name and the associated value are both passed in (Query.setString(String
name, String value)). Advantages of using named parameters are that the ordering of the components in the
where clause can change without affecting the query and that parameters can appear multiple times in a
query where the values should be the same.

Named queries also relieve an application of hard-coded column names and join specifics, which makes an application
resistant to change. As well, relying on named queries defined outside of the application's code means that the
application does not have to "know" Hibernate Query Language syntax, further insulating it from the persistence
mechanism which can more easily be changed without affecting the client code.

Criteria

The Criteria interface approaches querying by allowing a client to build an aggregation of query
clauses. Just as with the Query interface, the Hibernate Session acts as a factory for
Criteria, taking a Class reference of the entity class for which a Criteria
will be defined and returning an "empty" Criteria. The aggregation happens through the use of the
Expression class, which has methods for building discrete comparative expressions (like "greater than",
"less than", "like", "between", "equals", "and", "not", etc.) that can be found in regular SQL query strings. Static
factory methods on the Expression class return these Expression components, which can be
aggregated by the Criteria to form a composite query.

Wrapping the Query APIs

The simple data access layer described in the first half of this article acts to separate the client application
from the underlying persistence mechanism. Making the rich query capabilities available to a client application
while at the same time avoiding an explicit dependency on Hibernate means that the query APIs need to be wrapped by
a delegation layer. This layer need not provide any functionality in itself, other than exposing a simple way to
create queries and shielding the client from Session or other Hibernate specific details. Luckily,
Hibernate supports externalized queries (via the named query capability), so client applications can make use of
this through the wrapping layer to leverage powerful query facilities while leaving Hibernate's query language out
of the picture.

Putting It All Together

A review of the components described in this article shows that with an existing database definition, tools can
generate both Hibernate XML configuration files that provide a mapping between database entities and Java objects as
well as the source code for the Java object classes themselves. Furthermore, a lightweight data access layer can
provide a simple and generic means of leveraging the strengths and flexibilities of the Hibernate persistence
toolkit without imposing a compile time dependency on Hibernate for the client application. So what has this bought
us? A totally reusable persistence framework that makes use of code generation so that an application can evolve
over time, introducing new entities and relationships (or adjusting existing ones) with a minimum of hand-coding
necessary for support. The framework is also generic enough to be extended so that Hibernate need not be the
underlying data storage manager (or perhaps not the only data storage manager). Other O/R mapping or even
Java Data Objects (JDO) toolkits could be integrated into the framework in a transparent way to provide services
that Hibernate on its own may not.

Conclusion

There are an abundance of tools and technologies that provide solutions to problems that all data-driven
applications share, namely accessing and managing a data model. Two of those tools, Hibernate and Middlegen, combine
to automatically generate an object representation of an entity model. This article has demonstrated a simple data
access framework that leverages the strengths of Hibernate while providing a layer of separation so that an
application need not be dependent on its data access toolkit. The combination of code generation and toolkit
abstraction boosts an application's ability to quickly evolve and adjust as need warrants and more robust solutions
become available.

OCI partners with clients to assess, design, architect, engineer, manage and support Mission-Critical,
High Performance and Real Time systems. Our goal is to make IT solutions more open, scalable, reusable,
interoperable, and affordable. Please visit www.ociweb.com to learn
more about our service offerings, open source middleware technologies, and professional IT training.