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