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]