Implementing Generic DAO class for CRUD operations in Hibernate/JPA
There are three possible states in which an entity object can exist which are as follows;
Transient
Object:
An
entity
object
that was
never
persistent and
not
associated with any Session
is
called transient.
Persistent Object: An entity object that is persistent and associated with a unique Session is called persistent.
Detached Object: An entity object that was previously persistent and not associated with any Session is called detached.
Persistent Object: An entity object that is persistent and associated with a unique Session is called persistent.
Detached Object: An entity object that was previously persistent and not associated with any Session is called detached.
Note: While calling CRUD methods given in JPA EntityManager class or hibernate Session class we need to make sure that we always pass persistent or managed objects whenever needed. For example session.refresh() method does not require a persistent (i.e managed) entity object to be passed as parameter while entityManager.refresh() method does as for non-managed objects it will always throw
IllegalArgumentException
.Following is a plain generic dao interface with methods for basic CRUD operations which can easily be extended to include more methods for advance functionalities like searching, pagination and sorting etc.
// Generic DAO interface public interface IGenericDao<E, PK extends Serializable> { E create(E e); /* CREATE */ E read(PK id); /* READ */ E update(E e); /* UPDATE */ void delete(E e); /* DELETE */ E refresh(E e); /* Refresh data from the underlying database */ }
Following code snippet shows sample of hibernate native implementation for the above generic dao interface.
// Hibernate implementation public abstract class AbstractHibernateDao <E, PK extends Serializable> implements IGenericDao<E, PK> { private Session session = HibernateUtil.getSession(); private Class<E> type; public AbstractHibernateDao(Class<T> type) { this.type = type; validateType(); } @SuppressWarnings("unchecked") public AbstractHibernateDao() { this.type = (Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; validateType(); } private void validateType() { if (!this.type.isAnnotationPresent(Entity.class)) { throw new IllegalStateException( "Entity class should be annotated."); } } @Override public E create(E e) { this.session.save(e); return e; } @Override public E read(PK id) { return (E) this.session.get(type, id); } @Override public void update(E e) { this.session.update(e); } @Override public void delete(E e) { this.session.delete(e); } // Re-read the state of the given instance // from the underlying database. @Override public void refresh(E e) { this.session.refresh(e); } protected session getSession() { return this.session; } }
Now take a look at the following code snippet which shows sample of hibernate JPA implementation for the above generic dao interface.
//JPA implementation public abstract class AbstractJpaDao<E, PK extends Serializable> implements IGenericDao<E, PK> { private EntityManager entityManager; private Class<E> type; public AbstractJpaDao(Class<T> type) { this.type = type; validateType(); } @SuppressWarnings("unchecked") public AbstractJpaDao() { this.type = (Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; validateType(); } private void validateType() { if (!this.type.isAnnotationPresent(Entity.class)) { throw new IllegalStateException( "Entity class should be annotated."); } } @Override public E create(E e) { this.entityManager.persist(e); return e; } @Override public E read(PK id) { if (id == null) { return null; } return this.entityManager.find(type, id); } @Override public E update(E e) { e = this.entityManager.merge(e); // Flush changes to refresh optimistic-locking version value // which allows in-transaction operations // before commiting changes entityManager.flush(); return e; } @Override public void delete(E e) { // call merge first on the given object to avoid // IllegalArgumentException in case of detached entities e = this.entityManager.merge(e); this.entityManager.remove(e); } // Refresh the state of the instance from the database, // overwriting changes made to the entity, if any. @Override public void refresh(E e) { // call merge first on the given object to avoid // IllegalArgumentException in case of non-managed entities e = this.entityManager.merge(e); this.entityManager.refresh(e); } @PersistenceContext(unitName = "myPersistenceUnit") public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } protected EntityManager getEntityManager() { return entityManager; } }