Generic DAO and more

Since Java 5 introduced new generics feature many aspects of our Java coding has changed. DRY approach to many tiers in J2EE soared and become common place. Even such a conservative monsters as IBM come up with nice generic-based patterns. Have a look at this really good article Dont repeat the DAO. They did not invent something new but rather summarised bit and pieces in pretty well done generic DAO. However if you have simple web-based CRUD project you can go even further and generify your managers. Most of the time managers do exactly the same job providing additional wrapper around DAO. So it is the same CRUD as we had in DAO but DataBase -agnostic. So lets have a look at our DAO:

package com.maersk.hrb.dao;

import java.io.Serializable;
import java.util.List;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class GenericDao  extends HibernateDaoSupport {
    private Class modelClass;
    private SessionFactory sessionFactory;

public GenericDao(Class modelClass) {
        this.modelClass = modelClass;
    }

public PK create(T o) {
        return (PK) getHibernateTemplate().save(o);
    }

public T read(PK id) {
        return (T) getHibernateTemplate().get(modelClass, id);
    }

public void update(T o) {
    	getHibernateTemplate().update(o);
    }

public void delete(T o) {
    	getHibernateTemplate().delete(o);
    }

public List getObjects(DetachedCriteria criteria) {
		return getHibernateTemplate().findByCriteria(criteria);
	}

public List getObjects(Serializable[] ids) {
		return getHibernateTemplate().find("from "+modelClass.getSimpleName()+" alias where alias.id in ?", ids);
	}

public List getObjects() {
		return getHibernateTemplate().find("from "+modelClass.getSimpleName());
	}
}

Nothing new here. Well known generic DAO. I do not have any finder method here because my application is really simple. Anyway finders would bring big difference to idea.
So this is our generic manager:

package com.maersk.hrb.domain.logic;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.NotImplementedException;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;

import com.maersk.hrb.dao.GenericDao;
import com.maersk.hrb.domain.BaseObject;

public class GenericManager {
    private Class modelClass;
    private Class
pkClass;
	private GenericDao dao;

private static Map managersMap = new HashMap();

public GenericManager(Class modelClass, Class
pkClass) {
        this.modelClass = modelClass;
        this.pkClass = pkClass;
        this.dao = new GenericDao(modelClass);
        managersMap.put(modelClass, this);
    }

public static GenericManager lookupManagerByClass(Class modelClass){
    	return (GenericManager) managersMap.get(modelClass);
    }

public static GenericManager lookupManagerByClassName(String modelClassName){
    	Class modelClass;
		try {
			modelClass = Class.forName(modelClassName);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
    	return (GenericManager) managersMap.get(modelClass);
    }

public  static GenericManager lookupManagerBySimpleClassName(String modelClassName){
    	return lookupManagerByClassName("com.maersk.hrb.domain."+modelClassName);
    }

public void setSessionFactory(SessionFactory sessionFactory) {
		if (dao != null)
			dao.setSessionFactory(sessionFactory);
	}

public GenericManager(Class modelClass) {
    	this(modelClass, (Class
) Long.class);
    }

public Class getModelClass() {
		return modelClass;
	}

public void setDao(GenericDao dao) {
        this.dao = dao;
    }

public T getObject(String id) {
        try {
			Constructor
constructor = pkClass.getConstructor(String.class);
			PK newInstance = (PK)constructor.newInstance(id);
			return dao.read(newInstance);
		} catch (Exception e) {
			throw new RuntimeException("Wrong PK class");
		}
    }

public List getObjects(DetachedCriteria criteria) {
    	return dao.getObjects(criteria);
    }

public List getObjects(String[] ids) {
        return dao.getObjects(ids);
    }

public List getObjects(Serializable[] ids) {
        return dao.getObjects(ids);
    }

public T getObject(Long id) {
        return dao.read((PK)id);
    }

public List getObjects() {
        return dao.getObjects();
    }

public void removeObject(String id) {
        T object = getObject(id);
        dao.delete(object);
    }

public boolean isFieldValueUnique(String fieldName,Object fieldValue, T currentObject) {
    	throw new NotImplementedException("Not yet, sorry");
    }

public void saveObject(T object) {
    	if(object.isNew())
    		dao.create(object);
    	else
    		dao.update(object);
    }
}

Each manager creates his own DAO and registers itself in map. Note three lookup methods allowing us to get managers for specific model class. It is the sample of ‘Conventioin over Configuration’ principle. What the point of assigning managers to facade if we can explicitly get them when and where we need it? What is the point of defining Spring beans for DAO if the only place we use them is managers? The coupling here is not tough and I have no problem with that. Afterall coupling is evel thing when you try plug different implementations of modules, refactor, or anyhow else intoduce significant changes.
Despite of some coupling here we obviously not gonna have such problems. The only thing that I do not like here is sessionFactory. Managers only need sessionFactory to pass to their DAOs. In next post I’ll address such issues. Now lets continue to our spring beans definitions:

<bean id="managerTemplate"  abstract="true">
	<property name="sessionFactory">
		<ref bean="sessionFactory"/>
	</property>
</bean><bean id="staffMemberManager" class="com.maersk.hrb.domain.logic.GenericManager"  parent="managerTemplate">
	<constructor-arg>
		<value>com.maersk.hrb.domain.StaffMember</value>
	</constructor-arg>
</bean>

<bean id="aidQualificationManager" class="com.maersk.hrb.domain.logic.GenericManager" parent="managerTemplate">
	<constructor-arg>
		<value>com.maersk.hrb.domain.AidQualification</value>
	</constructor-arg>
</bean>

<bean id="remunerationEventManager" class="com.maersk.hrb.domain.logic.GenericManager" parent="managerTemplate">
	<constructor-arg>
		<value>com.maersk.hrb.domain.RemunerationEvent</value>
	</constructor-arg>
</bean>

<bean id="staffPositionHeldManager" class="com.maersk.hrb.domain.logic.GenericManager" parent="managerTemplate">
	<constructor-arg>
		<value>com.maersk.hrb.domain.StaffPositionHeld</value>
	</constructor-arg>
</bean>

Simple, short. No more scroll-blindness when looking at applicationContext.xml! This is what I wanted.

Advertisements

One Response to “Generic DAO and more”

  1. Franklin Samir Says:

    Congrats. This seems to be a good approach. I’ll try.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: