JavaBlog.fr / Java.lu DEVELOPMENT,Hibernate,Java Java/Hibernate: Don’t repeat the DAO with a GenericDao

Java/Hibernate: Don’t repeat the DAO with a GenericDao

Here, I would like to propose you a good practice concerning the implementation of GenericDao because the basic operations for data persistence are all identical independently of object to save, the methods in question are those of a CRUD: registration (Create), reading (Read), Update (Update) and deletion (Delete).
So, don’t repeat the DAO!!!! in order to increase our code’s productivity due to a GenericDao.

Generic DAO
Following, the steps to implement a generic DAO in ORM system:

  • Firstly, we need a generic DAO interface which contains the common CRUD available methods, our other specific DAO interfaces must extend it:
    public interface GenericDao<T, PK extends Serializable> {
    	PK create(T persistentObject);
    
    	T get(PK id);
    
    	List<T> getAll();
    
    	void update(T persistentObject);
    	
    	void createOrUpdate(T persistentObject);
    
    	void delete(T persistentObject);
    }
    
  • Then, the implementation of above interface is simple, but, the other DAO implementations will also extend it:
    @Transactional(propagation=Propagation.MANDATORY)
    public class GenericDaoImpl<T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> {
    
    	private Class<T> type;
    
    	public GenericDaoImpl(SessionFactory sessionFactory, Class<T> type) {
    		super.setSessionFactory(sessionFactory);
    		this.type = type;
    	}
    
    	@SuppressWarnings("unchecked")
    	public PK create(T o) {
    		return (PK) getSession().save(o);
    	}
    
    	@SuppressWarnings("unchecked")
    	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
    	public T get(PK id) {
    		T value = (T) getSession().get(type, id);
    		if (value == null) {
                return null;
            }
    
            if (value instanceof HibernateProxy) {
    			Hibernate.initialize(value);
    	        value = (T) ((HibernateProxy) value).getHibernateLazyInitializer().getImplementation();
            }
            return value;
    	}
    
    	@SuppressWarnings("unchecked")
    	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
    	public List<T> getAll() {
    		Criteria crit = getSession().createCriteria(type);
    		return crit.list();
    	}
    	
    	public void createOrUpdate(T o) {
    		if (o instanceof AbstractPersistentObject) {
    			if (((AbstractPersistentObject) o).isCreation()) {
    				getSession().saveOrUpdate(o);
    			} else {
    				getSession().merge(o);
    			}
    		} else {
    			throw new RuntimeException("this method support only AbstractPersistentObject");
    		}
    	}
    
    
    	public void update(T o) {
    		getSession().update(o);
    	}
    
    	public void delete(T o) {
    		getSession().delete(o);
    	}
    
    }
    

Specific DAO
Here, an example using the below class and interface in a specific DAO:

  • Example of a specific DAO interface that would can call the generic methods:

    public interface ChapterDao extends GenericDao<Chapter, String> {
    
    	/**
    	 * Find the chapter by number.
    	 */
    	public List<Chapter> findByNumber(String number) throws DataAccessException;
    ...
    }
    
  • Then, its implementation which contains the generic methods and the specific method ‘findByNumber’:
    @Repository("chapterDao")
    @Scope("singleton")
    public class ChapterDaoHibernateImpl extends GenericDaoImpl<Chapter, String> implements ChapterDao {
    
    	@Autowired
    	public ChapterDaoHibernateImpl(@Qualifier("sessionFactory") SessionFactory sessionFactory) {
    		super(sessionFactory, Chapter.class);
    	}
    
    	/**
    	 * Find the chapter by number.
    	 */
    	@SuppressWarnings("unchecked")
    	@Override
    	@Transactional(propagation=Propagation.MANDATORY, readOnly=true)
    	public List<Chapter> findByNumber(String number) throws DataAccessException {
    		StringBuffer hql = new StringBuffer("select chapter from Chapter chapter ");
    		hql.append(" where chapter.number=:number ");
    		Query query = getSession().createQuery(hql.toString());
    		//
    		query.setString("number", number);
    		List<Chapter> chapters = query.list();
    		return chapters;
    	}
    
    ...
    }
    

These classes and interfaces are built around the best pratices defined in the section HashCode, Equals methods and ORM layer of the post Java/Hibernate: HashCode, Equals methods.
So, our POJO must extend an abstract class with the HashCode, Equals methods overridden in order to compare correctly two objects and containing also a ‘id’ and ‘version’ attributes:

/**
 * AbstractPersistentObject
 * @author Huseyin OZVEREN
 */
public abstract class AbstractPersistentObject {
    private String id = IdGenerator.createId();

    private Integer version = null;


    public String getId() {
        return this.id;
    }

    public void setId(String value) {
        this.id = value;
    }

    public Integer getVersion() {
        return this.version;
    }

    public void setVersion(Integer value) {
        this.version = value;
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || !(o instanceof AbstractPersistentObject )) {
            return false;
        }
            
        AbstractPersistentObject other  = (AbstractPersistentObject ) o;
            
        // if the id is missing, return false
        if (id == null) return false;
            
        // equivalence by id
        return id.equals(other.getId());
    }

    public int hashCode() {
        if (id != null) {
            return id.hashCode();
        } else {
            return super.hashCode();
        }
    }

    public String toString() {
        return this.getClass().getName()
            + "[id=" + id + "]";
    }

    public boolean isCreation() {
        return version == null;
    }

    public void regenerateId() {
        id = IdGenerator.createId();
        version = null;
    }

    public AbstractPersistentObject() {
    }

...
}

The IdGenerator generates an unique Id:

public class IdGenerator {

    public static String createId() {
        UUID uuid = java.util.UUID.randomUUID();
        return uuid.toString();
    }
}

Example of business classes ‘Book’ and ‘Chapter’ in ORM layer:

public class Book extends AbstractPersistentObject {
    
	// ------------------------- ATTRIBUTS
    private String title;
    private String category = null;
    private int year;
    private boolean previouslyPublished = false;
    private String isbn10;
    private String isbn13;
    private Set<Author> authors = new HashSet<Author>();
    private double price = 0;
	private Language language = null;
	private Set<Chapter> chapters = new HashSet<Chapter>();
...
}
public class Chapter extends AbstractPersistentObject {
	
	// ------------------------------------------- ATTRIBUTS
	private Book book = null;
    private Calendar creationDate = null;
    private Calendar modifDate = null;
    private String title = null;
    private int number = 0;
    private String content = null;

    // ------------------------------------ GET/SET TERS
	public void setBook(Book book) {
		this.book = book;
	}
	
	public Book getBook() {
		book = ORMUtils.initializeAndUnproxy(book);
		
		return book;
	}
...
}

The source code of the GenericDaoImpl and GenericDao interface are in the ZIP file attachement.
This project needs the following librairies commons-beanutils-1.7.0.jar, commons-lang-2.4.jar, commons-logging-1.1.1.jar, hibernate-core-3.3.1.GA.jar, junit-4.1.jar and spring-2.5.5.jar.

Conclusion
A good practice in programming n-tier is to group queries into the service layer. The use of a generic DAO will reduce the number of code’s lines because all standard access methods are available in one class. So, the service layer will contains only the complex methods. More, all queries with HQL queries will be grouped in a single service object that corresponds to a domain object representing a module.

Download: test_ORMTools.zip

Best regards,

5 thoughts on “Java/Hibernate: Don’t repeat the DAO with a GenericDao”

  1. I needed to thank you for this wonderful read!! I definitely enjoyed every bit of it.

    I have got you saved as a favorite to check out new things you post…

Leave a Reply

Your email address will not be published.

Time limit is exhausted. Please reload CAPTCHA.

Related Post

Documentum : Audit trail entries dm_audittrail / SessionConfig / application_codeDocumentum : Audit trail entries dm_audittrail / SessionConfig / application_code

Hi, After my previous posts concerning the Documentum audit trail entries (dm_audittrail) http://www.javablog.fr/documentum-creation-of-audit-trail-entries-dm_audittrail.html and http://www.javablog.fr/deleting-of-audit-trail-entries-dm_audittrail.html, here, I would like to expose a solution in order to force content server to