當你偶然路過這里時,我假定你已經很明白
java
中范型和
DAO
模式了。當然,我也會順便嘮叨幾句范型和
DAO
模式,只是它們不會這篇隨筆的重點。我早先在
DW
上看到一篇蠻不錯的文章
不要重復
DAO!Hibernate 和 Spring AOP 構建泛型類型安全的 DAO
,它也促使我在一個實驗工程中使用了范型化的
DAO
模式。前幾天看到的另一篇文章
Generic Data Access Objects
使我重新想起了這檔事。以前的代碼不可追,索性就重做了一個
sample
實現范型化的
DAO
。坦白的講,和上面的兩篇文章相比,這篇隨筆并沒有太多新內容,如果你愿意的話,你可以只看上面的兩篇文章而關掉這個頁面。
先說說范型。自從
java5
引入范型后,它就成為我代碼中不可少的一部分。在沒有范型以前,強制轉換充斥于文件的各個角落,不單代碼不易于閱讀,時不時的會出現轉換異常。就像你所經歷的,對于集合類中的對象,如果顯示聲明的話,安全性和閱讀性都大大提高。總之啦,范型是個很有用的東西。
??????
再說說
DAO
模式。
Java EE
中,最經典的架構模式就是三層架構或多層架構。而數據持久化層,流行的就是
DAO
模式。盡管很多人批評這種
“
貧血模型
”
不
OO
,但使用上的方便使得
DAO
模式很受程序員喜愛。想一想
Spring
框架吧,支持
DAO
模式的典型框架,數據與操作的分離,使得
IOC
、
AOP
等技術靈活的運轉起來。說到底,理論和實踐并不是完全統一的,為了滿足實際的需要,有時不得不做些折衷。
好了,該說說我做的一個小例子。其實范型化的
DAO
就是給出一個抽象的
DAO
及其實現,實現的內容就是基本的
CRUD
操作。閑言少敘,開始我的代碼。
范型的
DAO
接口,包含基本的
CRUD
操作。
GenericDAO
的實現,通過
Spring
模板實現了
CRUD
操作。
import
?java.util.
*
;
import
?java.io.
*
;


public
?
interface
?GenericDAO
<
T,?PK?
extends
?Serializable
>
?
{

????T?findById(PK?id
);

????List
<
T
>
?findAll();

????
void
?insert(T?entity);
????
????
void
?update(T?entity);

????
void
?delete(T?entity);
}
??????
?package?org.prague.dao.hibernate;

import?java.util.*;
import?java.io.*;
import?org.hibernate.Session;
import?org.hibernate.SessionFactory;
import?org.springframework.orm.hibernate3.HibernateTemplate;
import?org.prague.dao.GenericDAO;
import?java.lang.reflect.*;

public?abstract?class?GenericHibernateDAO<T,?PK?extends?Serializable>

????????implements?GenericDAO<T,?PK>?
{

????private?HibernateTemplate?hibernateTemplate;
????private?Class<T>?type;
????

????public?GenericHibernateDAO()
{
????????this.type?=?(Class<T>)((ParameterizedType)(this.getClass().getGenericSuperclass()))
????????????????????????????.getActualTypeArguments()[0];
????}
????

????public?void?setSessionFactory(SessionFactory?sessionFactory)
{
????????this.hibernateTemplate?=?new?HibernateTemplate(sessionFactory);
????}
????

????public?void?delete(T?entity)?
{
????????this.hibernateTemplate.delete(entity);
????}
????
????


????public?List<T>?findAll()?
{
????????String?hql?=?"from?"+this.type.getSimpleName();
????????return?(List<T>)this.hibernateTemplate.find(hql);
????}


????public?T?findById(PK?id)?
{
????????return?(T)this.hibernateTemplate.get(type,?id);
????}


????public?void?insert(T?entity)?
{
????????this.hibernateTemplate.save(entity);
????}


????public?void?update(T?entity)?
{
????????this.hibernateTemplate.update(entity);
????}
????

????protected?Session?getSession()
{
????????return?this.hibernateTemplate.getSessionFactory().getCurrentSession();
????}
????

????protected?HibernateTemplate?getHibernateTemplate()
{
????????return?this.hibernateTemplate;
????}
????
}??????
如果不用Spring的話,Hibernate也能輕松的實現這些操作。但坦白的講,這些操作并不實用。對于不同的實體,可能不會調用這些操作或是調用的操作不完全相同。比如說,對于查詢數據表中的數據列表,可能的情況是要求查詢條件中按照某些字段排序。如果想給出一個通用的實現方法,接口中不可避免的要包含查詢條件,比如說包含一個Hibernate中的條件查詢對象。但這樣話,就需要在Service層構造查詢條件,還不如直接寫個方法來的實在。
???
下面就是一個具體的DAO及實現。
import?org.prague.domain.*;


public?interface?AccountDAO?extends?GenericDAO<Account,Long>
{
????public?Account?findByNameAndPwd(String?name,String?pwd);
}
package?org.prague.dao.hibernate;

import?java.util.List;

import?org.prague.dao.AccountDAO;
import?org.prague.domain.Account;
import?org.springframework.orm.hibernate3.HibernateCallback;
import?org.springframework.orm.hibernate3.HibernateTemplate;

public?class?AccountHibernateDAO?extends?GenericHibernateDAO<Account,Long>?implements

????????AccountDAO?
{


????public?Account?findByNameAndPwd(final?String?name,?final?String?pwd)?
{
????????final?String?hql??=?"from?Account?where?name=:name?and?pwd=:pwd";

????????/**//*
????????List<Account>?list?=?(List<Account>)this.getHibernateTemplate().executeFind(new?HibernateCallback(){
????????????public?Object?doInHibernate(Session?s)?throws?HibernateException,?SQLException?{
????????????????return?s.createQuery(hql).setString("name",?name).setString("pwd",?pwd).list();
????????????}
????????});
????????*/
????????List<Account>?list?=?(List<Account>)this.getHibernateTemplate().findByNamedParam(hql,?

????????????????new?String[]
{"name","pwd"},?new?String[]
{name,pwd});

????????if(list!=null?&&?!list.isEmpty())
{
????????????return?list.get(0);
????????}
????????return?null;
????}
}???????當然少不了實體bean了,盡管它很簡單。
package?org.prague.domain;

import?java.io.Serializable;


public?abstract?class?AbstractDomain<PK?extends?Serializable>
{
????protected?PK?id;

????

????public?PK?getId()?
{
????????return?id;
????}


????public?void?setId(PK?id)?
{
????????this.id?=?id;
????}

}

package?org.prague.domain;


public?class?Account?extends?AbstractDomain<Long>
{
????private?String?name;
????private?String?pwd;
????

????public?String?getName()?
{
????????return?name;
????}

????public?void?setName(String?name)?
{
????????this.name?=?name;
????}

????public?String?getPwd()?
{
????????return?pwd;
????}

????public?void?setPwd(String?pwd)?
{
????????this.pwd?=?pwd;
????}
????@Override

????public?String?toString()?
{
????????return?"Account:?id="+this.id+"?name="+this.name+"?pwd="+this.pwd+"\n";
????}
????
}???????想要程序運行起來,還需要配置文件的作用,這里列出Account對應的Hibernate映射文件和Spring配置文件。由于該Sample沒有Service層的概念,因此DAO需要聲明性事務的作用。
<?xml?version='1.0'?encoding='UTF-8'?>
<!DOCTYPE?hibernate-mapping?PUBLIC?"-//Hibernate/Hibernate?Mapping?DTD?3.0//EN"?
????"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping?package="org.prague.domain">
????<class?name="Account"?table="Account">
????????<id?name="id"?column="id"?type="java.lang.Long">
????????????<generator?class="identity"></generator>
????????</id>
????????<property?name="name"?column="name"></property>
????????<property?name="pwd"?column="pwd"></property>
????</class>
</hibernate-mapping>?
<?xml?version="1.0"?encoding="UTF-8"?>
<!DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN//EN"?"http://www.springframework.org/dtd/spring-beans.dtd">
<beans?default-lazy-init="true">

????<bean?id="sessionFactory"
????????class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
????????<property?name="configLocation">
????????????<value>classpath:hibernate.cfg.xml</value>
????????</property>
????</bean>

????<bean?name="transactionManager"
????????class="org.springframework.orm.hibernate3.HibernateTransactionManager">
????????<property?name="sessionFactory">
????????????<ref?bean="sessionFactory"?/>
????????</property>
????</bean>

????<bean?id="baseTransactionProxy"
????????class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
????????abstract="true">
????????<property?name="transactionManager"?ref="transactionManager"?/>
????????<property?name="transactionAttributes">
????????????<props>
????????????????<prop?key="get*">PROPAGATION_REQUIRED,readOnly</prop>
????????????????<prop?key="*">PROPAGATION_REQUIRED</prop>
????????????</props>
????????</property>
????</bean>

????<bean?id="accountDAO"?parent="baseTransactionProxy">
????????<property?name="target">
????????????<bean?class="org.prague.dao.hibernate.AccountHibernateDAO">
????????????????<property?name="sessionFactory">
????????????????????<ref?bean="sessionFactory"?/>
????????????????</property>
????????????</bean>
????????</property>
????</bean>
</beans>???????再多說幾句吧。我以前寫過一個程序,在GenericDAO中給出了更多的方法,比如:
public?List<T>?findListBy(String?expression);
????public?void?deleteBy(String?expression);
????public?void?updateBy(String?expression);??? 我的想法和做法是不言而喻的。當時想通過在GenericDAO中聲明這樣的公共方法,簡化DAO中操作。那是我還不是很清楚Hibernate中的Creteria,就實現一個類似于Hibernate Creteria包的東西,通過在Service層中構造Creteria對象并得到一個拼接后的hql語句,然后調用上面的這幾個方法。通過上面的幾個方法,基本上滿足了DAO層的操作。但問題是,我不得不在Service層構建Creteria,Service因此顯得臃腫異常,而DAO層就單薄得很。好好的想一想,這樣的結果不過是變相的將DAO層和Service層合并了。依我的想法,刻意的追求公共數據庫操作并不一定實用。即便是這個例子,如果不使用Hibernate框架,而是使用JDBC、Ibatis等,GenericDAO 中的方法是很難以通用的方式實現的。你不得不做的就是,每個繼承自 GenericDAO的DAO,都需要單獨的實現基本的CRUD操作。??
?