边城愚人

如果我不在边城,我一定是在前往边城的路上。

  语源科技BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  31 随笔 :: 0 文章 :: 96 评论 :: 0 Trackbacks

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

 

posted on 2007-04-17 08:55 kafka0102 阅读(3700) 评论(7)  编辑  收藏 所属分类: Pattern

评论

# re: 要范型,要DAO,更要范型DAO[未登录] 2007-04-17 09:07 Matthew
朋友,把排版重搞一下吧!  回复  更多评论
  

# re: 要范型,要DAO,更要范型DAO 2007-04-17 09:20 kafka0102
都要疯掉了。昨晚第一遍写的时候,直接在编辑器里做,结果添加代码框出不来,IE死掉了。第二遍在word里写,结果发表时不能连接服务器。好吧,一大早来做,结果从word到编辑器转换时,格式都乱了。就这样了,没心情再改了。  回复  更多评论
  

# re: 要范型,要DAO,更要范型DAO 2007-04-17 10:32 CrossAge
这样子做,还有必要搞DAO这一层么?
目的就是为了节省CRUD的代码量?  回复  更多评论
  

# re: 要范型,要DAO,更要范型DAO 2007-04-18 08:40 开源英汉机器翻译
开源英汉机器翻译C#.NET项目 www.liebiao.net

我们邀请你 技术交流  回复  更多评论
  

# re: 要范型,要DAO,更要范型DAO[未登录] 2007-04-27 13:20 Diego
楼主,你博客上的的代码折叠功能是用什么工具生成的?我问了好几个人都不知道,我也想要这个功能  回复  更多评论
  

# re: 要范型,要DAO,更要范型DAO 2007-05-03 22:06 kafka0102
发表文章时,代码不是直接贴进去的,而是选择编辑器中的“代码”选项(应该在编辑菜单的最后一个位置),然后根据弹出来的对话框进行操作就行了。  回复  更多评论
  

# re: 要范型,要DAO,更要范型DAO 2008-10-31 11:14 天山剑
需要一个范型dao,

上面的有问题,测试不了

有没有测通的

请高手指点

QQ:328152797

  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: