在正式接觸Generics之前,看一些Sun所提供的Sample Code,還以為Generics唯一的好處就只是能夠針對Collection類型的資料結構宣告內容物的型別,像是這樣:
ArrayList<Person>但是最近看到Hibernate的始作俑者─Gavin King─在他的Blog裡頭提到一種Generic Hibernate DAO的想法,才驚覺其實Generics擁有更大的用途。正確的使用Generics能夠減少很多必要的程式碼,而最顯而可見的地方,就是我們經常用到的Data Access Object(DAO Pattern)。anArrayList = new ArrayList();
anArrayList.add( new Person() );
Person aInstanceHolder = anArrayList.get(0);
一般在使用DAO的時候,其實是必須針對每個Domain Object進行DAO相關的介面制定以及實作;坦白說有時候真的會覺得自己真是白痴,明明寫的CRUD Operation大同小異,但是就是沒辦法將這些程式碼整理成可重複利用的單位。
在Gavin King的Blog裡頭,我找到了能夠將DAO程式碼大幅減少的解決方案:Generic Hibernate DAO Pattern。但是由於Gavin King所提供的Sample Code所使用的API是Hibernate 3的Session API,所以我把這個Pattern的Code稍微潤飾一下,改成適合和Spring Framework可以一起搭配使用的Code。以下就是安東尼版本的「Generic DAO Pattern」。
首先,我們先定義一個Generic DAO Interface。既然是Interface,那麼顧名思義,是不會對任何Persistence API擁有Binding關係的:
public interface GenericDao在上面的介面定義當中,我們可以看到大量Generics的影子;而這段Code當中使用Generics最關鍵的地方就在於Interface宣告搭配Generics。我們待會會看到這樣的宣告方式搭配了物件導向的繼承/實作之後所發揮的驚人能力;在那之前,我們再定一一個GenericHibernateDao的抽象類別:<T, ID extends Serializable>{
T findById( ID id, boolean lock );
List<T>findAll(); <T>
ListT findByExample( T exampleInstance );
T makePersistent( T entity );
void makeTransient(entity );
}
public abstract class GenericHibernateDao<T, ID extends Serializable> extendsOK,我們又看到了大量的Generics,以及對Spring Framework Hibernate Support API的一些呼叫。可能有些人已經開始發現一些端倪了:在這個抽象類別當中所定義的CRUD操作,任何繼承的Class都能夠在幾乎不需要寫Code的情晃下「免費」使用:
HibernateDaoSupport implements GenericDao<T,ID> {
private ClasspersistentClass; List
public GenericHibernateDao() {
this.persistentClass = (Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
public ClassgetPersistentClass() {
return persistentClass;
}
@SuppressWarnings("unchecked")
public T findById( ID id ) {
T entity;
entity = (T) getHibernateTemplate().load( getPersistentClass(), id );
return entity;
}
@SuppressWarnings("unchecked")
public List<T> findAll() {
return getHibernateTemplate().loadAll( getPersistentClass() );
}
@SuppressWarnings("unchecked")
public<T> List findByExample( T exampleInstance ) {
return findByCriteria(Example.create( exampleInstance ));
}
@SuppressWarnings("unchecked")
public T makePersistent( T entity ) {
getHibernateTemplate().saveOrUpdate( entity );
return entity;
}
public void makeTransient( T entity ) {
getHibernateTemplate().delete( entity );
}
/**
* Use this inside subclasses as a convenience method.
*/
@SuppressWarnings("unchecked")
protected<T> findByCriteria( Criterion... criterion ) {
DetachedCriteria crit = DetachedCriteria
.forClass( getPersistentClass() );
for (Criterion c : criterion) {
crit.add( c );
}
return getHibernateTemplate().findByCriteria( crit );
}
}
public interface AuthorDao extends GenericDao<Author,Integer>你並沒有看錯,就只要寫這些宣告,你就可以免費使用剛剛在GenericHibernateDao所定義的所有CRUD Operation。{
}
public class AuthorDaoHibernateImpl extends GenericHibernateDao<Author,Integer>implements AuthorDao<Author, Integer> {
}
這是怎麼做到的?
讀者只要注意一下Sample Code當中用藍色以及紅色的部分所加強部分就可以猜出來了:我們把「T,ID」的實際型別透過繼承宣告「丟」進去,讓Concrete Class在借用GenericHibernateDao的操作時,JVM能夠分辨的出來到底實體的Class Instance是什麼。很神奇吧!
Technorati Tags: java, hibernate, generics
No comments:
Post a Comment