当你偶然路过这里时,我假定你已经很明白 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 list = (List)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操作。