The “Java Persistence API”:http://en.wikipedia.org/wiki/Java_Persistence_API provides a programming language framework for managing “relational data”:http://en.wikipedia.org/wiki/Relational_model through “entity management”:http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html and “ORM”:http://en.wikipedia.org/wiki/Object-relational_mapping APIs. Libraries like “Hibernate”:http://hibernate.org/ and “Guice Persist”:http://code.google.com/p/google-guice/wiki/GuicePersist implement the services defined in the JPA specification. The result is an abstraction layer that manages the underlying concerns of a “relational database”:http://en.wikipedia.org/wiki/Relational_database while providing a consistent API that doesn’t vary from one database to another.
However, even with a database abstraction layer, there are instances where the concerns of the database are also the concerns of the programmer. Moreover, these concerns can affect a number of areas in an application, and addressing them often leads to code duplication.
“Aspect-oriented programming”:http://en.wikipedia.org/wiki/Aspect-oriented_programming (AOP) is a programming paradigm that addresses “cross-cutting concerns”:http://en.wikipedia.org/wiki/Cross-cutting_concern and eliminates the need for code duplication. “Guice”:http://code.google.com/p/google-guice/ has built-in support for “method interception”:http://code.google.com/p/google-guice/wiki/AOP that brings the AOP programming paradigm to life.
“Transactions”:http://en.wikipedia.org/wiki/Database_transaction are commonplace in database programming and are often required when updating or saving an entity. Transactions are a _cross-cutting_ concern, which means programmers may find themselves dealing with transactions throughout their codebase.
For example, we may want to encapsulate any change made to an entity in a transaction where we commit it on success and rollback any changes if we encounter an error.
The code above is _concerned_ with persisting an entity to the database. However, the bulk of code is dealing with creating, starting, and either committing or rolling back a transaction.
Luckily for us, “Guice Persist”:http://code.google.com/p/google-guice/wiki/GuicePersist has provided a solution to this problem where our above code is reduced to:
Transactional annotation indicates that the
save method should be wrapped in a transaction (a subclass is generated at runtime where a new method is defined that wraps a transaction around the original save implementation). The save method is now only concerned with persisting the entity and isn’t dealing with any extraneous concerns.
h2. An Example Problem
h3. Stale Data: A Cross-Cutting Concern
A common issue that we’ve come across using JPA and Hibernate is that data can become stale and out-of-date over a period time. The main reason for this is that entity managers are _not_ thread-safe. The solution to this issue is to use “thread local”:http://en.wikipedia.org/wiki/Thread-local entity managers, which in turn maintain their _own_ persistence context.
It’s likely that entities in one persistence context may differ from another persistence context across different threads. Until they are _explicitly_ refreshed and synchronized with the database, they’ll remain out-of-date or stale. For example, one entity may have had its name updated, and entities that it owns could have been removed. It will remain unaware of these changes until it has been refreshed.
h3. An Initial Solution
In order to ensure that entities are up-to-date, we need to make sure they are refreshed before using them:
The problem with this particular solution is that each time we lookup and return an entity or set of entities, we have to duplicate the code that refreshes the entities.
h3. One Step Further
The next step would be to abstract the refresh code into a helper method (or delegate it to another object):
The above solution is obviously DRYer than our first one. However, we are still _concerning_ ourselves with refreshing entities while our primary responsibility lies in looking up entities. Additionally, this particular solution only works for
SchoolBus entities and would require creating similar code for other entity types.
h2. Aspect-Oriented Solution
The problem with our above solutions is that we are duplicating code and forcing the code to address the refreshing of entities.
We want a solution that:
* Doesn’t require that we duplicate code.
* Allows the code to focus on its primary concern by automatically refreshing the entity and any entities it may own.
* Works across a heterogenous set of entities.
…and looks like:
While the transaction example in the previous section seems somewhat “magical,” it’s actually relatively straightforward to implement our own AOP solution. It requires that we create an “annotation”:http://en.wikipedia.org/wiki/Java_annotation, a class that implements the “MethodInterceptor”:http://aopalliance.sourceforge.net/doc/org/aopalliance/intercept/MethodInterceptor.html interface, and setup “Guice bindings”:http://code.google.com/p/google-guice/wiki/Bindings in a injector module.
h3. The RefreshEntity Interface
h3. The Method Interceptor
The full implementation of the method interceptor can be found here. The
RefreshEntityInterceptor ensures that any entity retrieved is properly refreshed before it is returned to its caller.
Specifically, it does the following:
# Invokes the method annotated with the
# Determines whether the result is a single entity or a collection of entities.
# Clears any collections that are defined as
# Refreshes the entity and returns it.
h3. Guice Bindings
In our injector module, we need to create an interceptor binding to ensure that any methods that are annotated with
RefreshEntity are dynamically subclassed and wrapped by the
Even with a _relatively_ nice data abstraction layer, there instances where the concerns of the database or the entity management layer become the concern of the programmer as well. Using the Aspect-oriented programming techniques and Guice, we can isolate those concerns into one or a few areas and keep the rest of our codebase relatively free of data layer issues.