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;
}
}