Using Generics to Simplify Hibernate/Spring DAOs (update #2 for JPA)

I’ve been working with JPA and Hibernate Annotations and needed to update my base DAO interface and implementation class from its native Hibernate implementation.

The interface is a little different from the one I’d been using so I’m including the new one:

package com.foo.bar.dao;

import java.io.Serializable;
import java.util.Collection;

import com.foo.bar.model.DatabaseObject;

/**
 * Base DAO.
 * @author Burt
 * @param <T> the entity type
 */
public interface BaseDAO<T extends DatabaseObject> {

  /**
   * Get the total number of instances.
   * @return  the count
   */
  long getTotalCount();

  /**
   * Find all.
   * @return  all instances
   */
  Collection<T> findAll();

  /**
   * Find an instance by primary key.
   * @param pk  the primary key
   * @return  the instance
   */
  T findByPrimaryKey(Serializable pk);

  /**
   * Insert a new instance.
   * @param t  the new instance
   * @return  the instance updated with its primary key
   */
  T create(T t);

  /**
   * Save changes to an existing instance.
   * @param t  the instance
   * @return  the updated & refreshed instance
   */
  T update(T t);

  /**
   * Delete an existing instance.
   * @param t  the instance
   */
  void delete(T t);
}

and the new base implementation class:

package com.foo.bar.dao.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Date;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.foo.bar.annotation.DependencyInjection;
import com.foo.bar.dao.BaseDAO;
import com.foo.bar.model.DatabaseObject;

/**
 * Abstract base class for Hibernate DAOs.
 *
 * @author Burt
 * @param <T> the entity type
 */
@Transactional
@Repository
public abstract class BaseDaoImpl<T extends DatabaseObject>
       implements BaseDAO<T> {

  protected final Logger _log = Logger.getLogger(getClass());

  private Class _entityClass;

  private EntityManager _entityManager;

  /**
   * Inject the entity manager.
   * @param entityManager
   */
  @PersistenceContext
  @DependencyInjection
  public void setEntityManager(final EntityManager entityManager) {
    _entityManager = entityManager;
  }
  protected EntityManager getEntityManager() {
    return _entityManager;
  }

  /** (non-Javadoc)
   * @see com.foo.bar.dao.BaseDAO#getTotalCount()
   */
  public long getTotalCount() {
    return (Long)getEntityManager().createQuery(
      "SELECT COUNT(*) FROM " + getEntityClass().getSimpleName())
      .getSingleResult();
  }

  /** (non-Javadoc)
   * @see com.foo.bar.dao.BaseDAO#findAll()
   */
  @SuppressWarnings("unchecked")
  public Collection<T> findAll() {
    return getEntityManager().createQuery(
        "FROM " + getEntityClass().getName())
        .getResultList();
  }

  /** (non-Javadoc)
   * @see com.foo.bar.dao.BaseDAO#findByPrimaryKey(java.io.Serializable)
   */
  @SuppressWarnings("unchecked")
  public T findByPrimaryKey(final Serializable pk) {
    return (T)getEntityManager().find(getEntityClass(), pk);
  }

  /** (non-Javadoc)
   * @see com.foo.bar.dao.BaseDAO#create(com.foo.bar.model.DatabaseObject)
   */
  public T create(final T t) {
    getEntityManager().persist(t);
    return t;
  }

  /** (non-Javadoc)
   * @see com.foo.bar.dao.BaseDAO#update(com.foo.bar.model.DatabaseObject)
   */
  public T update(final T t) {
    t.setModified(new Date());
    getEntityManager().merge(t);
    return findByPrimaryKey(t.getPk());
  }

  /** (non-Javadoc)
   * @see com.foo.bar.dao.BaseDAO#delete(com.foo.bar.model.DatabaseObject)
   */
  public void delete(final T t) {
    getEntityManager().remove(t);
  }

  protected synchronized Class getEntityClass() {

    if (_entityClass == null) {
      Type type = getClass().getGenericSuperclass();
      loop:
      while (true) {
        if (type instanceof ParameterizedType) {
          Type[] arguments =1) {
              _entityClass = (Class)argument;
              break loop;
            }
          }
        }
        type = ((Class)type).getGenericSuperclass();
        if (type == Object.class) {
          throw new RuntimeException(
              "Could not find a DatabaseObject subclass parameterized type");
        }
      }
    }
    return _entityClass;
  }
}
  1. ParameterizedType)type).getActualTypeArguments(); for (Type argument : arguments) { if (argument instanceof Class && DatabaseObject.class.isAssignableFrom(((Class)argument[back]

3 Responses to “Using Generics to Simplify Hibernate/Spring DAOs (update #2 for JPA)”

  1. Burt,

    It is generally a really bad idea to make your individual DAOs transactional like this.

    This is because when some other code is working with the DAO or multiple DAOs, to do a set of related operations, each operation will be its own separate transaction, instead of happening in _one_ transaction, as you actually need for ACIDity.

    Generally, you need to make the code that is using the DAOs (generally the service layer) transactional instead.

    Colin

  2. Burt says:

    That would be true if it were specified as REQUIRES_NEW, but the default propagation is REQUIRED (sorry if it looks like I’m yelling) so a new transaction would be started if none existed, and a current transaction would be joined.

    The typical use of a pattern like this is to have a coarse-grained manager class (facade) call the fine-grained DAO classes, where the manager class has more specific transaction specifications (methods that don’t access the database wouldn’t be transactional) and would wrap multiple DAO calls (most of which are usually transactional) in a single transaction.

    Plus you can override the transaction attribute in base classes and for individual methods.

  3. […] An Army of Solipsists » Blog Archive » Using Generics to Simplify Hibernate/Spring DAOs (update #2 for JPA) (tags: java) […]

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.