We're hiring!

We're actively seeking developers for our new Detroit location. Learn more

Guice AOP and the Java Persistence API

The Java Persistence API provides a programming language framework for managing relational data through entity management and ORM APIs. Libraries like Hibernate and Guice Persist implement the services defined in the JPA specification. The result is an abstraction layer that manages the underlying concerns of a 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 (AOP) is a programming paradigm that addresses cross-cutting concerns and eliminates the need for code duplication. Guice has built-in support for method interception that brings the AOP programming paradigm to life.

Transactions

Transactions 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.

public void save(Object entity) {
    final EntityTransaction transaction = getEntityManager().getTransaction();
    transaction.begin();
    try {
        getEntityManager().persist(entity);
        transaction.commit();
    } catch (Throwable e) {
        transaction.rollback();
        throw new RuntimeException(e);
    }
}

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 has provided a solution to this problem where our above code is reduced to:

@Transactional
public void save(Object entity) {
    getEntityManager().persist(entity);
}

…where the 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.

An Example Problem

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 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.

An Initial Solution

In order to ensure that entities are up-to-date, we need to make sure they are refreshed before using them:

public List<SchoolBus> findAll() {
    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
    CriteriaQuery<SchoolBus> criteriaQuery = criteriaBuilder.createQuery(SchoolBus.class);
    Root<SchoolBus> table = criteriaQuery.from(SchoolBus.class);
    TypedQuery<SchoolBus> query = getEntityManager().createQuery(criteriaQuery.select(table).orderBy(criteriaBuilder.asc(table.get("busNumber"))));
    /*
    / Refresh all the school buses
    */
    for (SchoolBus schoolBus : query.getResultList()) {
        schoolBus.getStudents().clear();
        getEntityManager().refresh(schoolBus);
    }
    return query.getResultList();
}

public SchoolBus findById(Long id) {
    SchoolBus schoolBus = getEntityManager().find(SchoolBus.class, id);
    /*
    / Refresh school bus
    */
    schoolBus.getStudents().clear();
    getEntityManager().refresh(schoolBus);    
    return schoolBus;
}

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.

One Step Further

The next step would be to abstract the refresh code into a helper method (or delegate it to another object):

public List<SchoolBus> findAll() {
    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
    CriteriaQuery<SchoolBus> criteriaQuery = criteriaBuilder.createQuery(SchoolBus.class);
    Root<SchoolBus> table = criteriaQuery.from(SchoolBus.class);
    TypedQuery<SchoolBus> query = getEntityManager().createQuery(criteriaQuery.select(table).orderBy(criteriaBuilder.asc(table.get("busNumber"))));
    refreshSchoolBuses(query.getResultList());
    return query.getResultList();
}

public SchoolBus findById(Long id) {
    SchoolBus schoolBus = getEntityManager().find(SchoolBus.class, id);
    refreshSchoolBuses(schoolBus);
    return schoolBus;
} 

private void refreshSchoolBuses(SchoolBus... schoolBuses) {
    refreshSchoolBuses(Arrays.asList(schoolBuses));
}

private void refreshSchoolBuses(List<SchoolBus> schoolBuses) {
    for (SchoolBus schoolBus : schoolBuses) {
        schoolBus.getStudents().clear();
        getEntityManager().refresh(schoolBus);
    }
}

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.

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:

@RefreshEntity
public List<SchoolBus> findAll() {
    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
    CriteriaQuery<SchoolBus> criteriaQuery = criteriaBuilder.createQuery(SchoolBus.class);
    Root<SchoolBus> table = criteriaQuery.from(SchoolBus.class);
    TypedQuery<SchoolBus> query = getEntityManager().createQuery(criteriaQuery.select(table).orderBy(criteriaBuilder.asc(table.get("busNumber"))));
    return query.getResultList();
}
                    
@RefreshEntity
public SchoolBus findById(Long id) {
    SchoolBus schoolBus = getEntityManager().find(SchoolBus.class, id);
    return schoolBus;
} 

Implementation

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, a class that implements the MethodInterceptor interface, and setup Guice bindings in a injector module.

The RefreshEntity Interface

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RefreshEntity {
}

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:

  1. Invokes the method annotated with the RefreshEntity interfaces.
  2. Determines whether the result is a single entity or a collection of entities.
  3. Clears any collections that are defined as OneToMany relationships.
  4. Refreshes the entity and returns it.

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 RefreshEntityInterceptor.

@Override
protected void configure() {
    RefreshEntityInterceptor refreshEntityInterceptor = new RefreshEntityInterceptor();
    requestInjection(refreshEntityInterceptor);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(RefreshEntity.class), refreshEntityInterceptor);
}

Conclusion

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.
 

Justin DeWind (42 Posts)

This entry was posted in Web Apps and tagged , . Bookmark the permalink. Both comments and trackbacks are currently closed.

One Comment

  1. Mihai A
    Posted November 7, 2012 at 8:14 am

    you need to call UnitOfWork.end() to close the EM at the end of your request/transaction(whatever) and you won’t have the problem anymore