Pages

Friday, 11 January 2013

Hibernate as Persistence Provider and ORM Solution - V


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.

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