Using Generics to Simplify Hibernate/Spring DAOs (update #2 for JPA)
Thursday, March 22nd, 2007I’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; } }
- ParameterizedType)type).getActualTypeArguments(); for (Type argument : arguments) { if (argument instanceof Class && DatabaseObject.class.isAssignableFrom(((Class)argument[back]