论坛首页 Java企业应用论坛

一次关于简化DAO设计的初步思考!

浏览 40561 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-10-10  
DAO
当应用中需要使用到上十张表时,DAO的维护变得日益困难,主要表现在这几个方面:
1)dao类的繁多,很多设计都是一个entity对应一个dao
2)dao接口需要维护的method庞大。
3)业务逻辑改变时,dao需要同时修改两个类文件(接口和实现类)

出于上述问题,有必要从新设计dao包。要求
1)减少dao类的数目,dao包暴露的接口稳定且易于扩展新的查询。
2)减少dao接口的method
3)可以动态增加dao提供的数据访问逻辑,比如增加相应的查询实现等等

我的分析:
可以使用Criteria,NamedQuery来构造复杂的查询逻辑。
对于改动最多的read/find逻辑,可以作一个抽象设计,以提供动态增加的同时提供稳定的接口。
对于update,create,delete逻辑,dao的设计遵循原子操作的原则,不在这些接口中实现复杂的业务逻辑。总体来说,这些接口的改动很少,也很容易规范。

下面是一些设计的类代码:
public interface QueryObject{
     public void setNamedQuery(String qstring);;
     public Object query(Session session,Set parameterCollection);;
     public QueryCfg getQueryCfg();;
     public boolean validate(QuryCfg cfg,Set params); ;
     public void setValidatable(boolean bool);;
     public booelan getValidatable();;
}

public interface QueryCfg{
    public List getParameterCfgs();;
    public String getName();;
    public void setName(String name);;
}


public interface Parameter{
  public ParameterCfg getParameterCfg();;
   public String getValue();;
   
}

public interface ParameterCfg{
    public int getOrder();;
    public void setOrder(int order);;
    public String getName();;
    public void setName(String name);;
    public  Type getType();;
    public void setType();;
       
}

public interface Type{
   public static final String PRIMITIVE_TYPE="primitive";
   public boolean isPrimitive();;
   public Class getObjectType();;
   public String getValue(Object o);;
}




上面这些基本接口便是组成我的dao包的设计基架。虽然与普通dao实现比较起来,这个设计多了许多复杂度,在项目初期也需要一定工作量来编码实现这些构架,但是当在项目进展到中后期时,这个设计已经显露了它优良的设计风格,它与普通DAO的设计相比较,有如下优点:
1)DAO的设计将会更加简洁,用一个DAO接口就可以实现所有entity的dao操作,可以动态扩展复杂查询。DAO设计:
public interface DAO{
   // find 可以根据
   public Object find(String namedQuery,Set parameters);;
   public Object createEntity(Object entity);;
   public Object updateEntity(Object entity);;
   public void removeEntity(Object entity);;
}

//具体的实现可以整合到优秀的容器里面。比如spring
publi class DAOImpl{
 //....
  public Object find(String name,Set ps);{
		return (Object);getHibernateTemplate();.execute(new HibernateCallback(); {
			public Object doInHibernate(Session session); throws HibernateException {
                              return QueryLocator.getQueryObject(name);.query(session,ps);;
		});;			
      
 }
}
//其中QueryLocator的实现可以用Spring,PicoContainer等等。


2)业务逻辑层依赖于一个较稳定的DAO接口,使得可以更独立更快速的进行开发。
在业务逻辑层,他们可以这样写代码:

public class YourBusiness{
   public void doBusiness();{
       Set parameters=new LinkedHashSet();;
        parameters.add("firstName","test");;
        parameters.add(2,"test2");;
       List users=(List);getDao.find("findUserWithFullName",parameters);;
       
}
}

这里的代码依赖于一个稳定的DAO接口,而不需要先实现DAO修改,再回来做相应的业务实现。
3)可以支持参数检查,因为dao包的设计中加入了诸如ParameterCfg,QueryCfg等配置类,通过扩展这些抽象配置类实现我们定义的查询的配置,可以有效地帮助我们检查客户端的非法查询。
4)工作量统计。可以计算一下,dao增加一个新的复杂的查询,需要增加一个QureyCfg类(可选,如果queryObject支持验证参数,则需要否则不需要),一个QueryObject类,增加Spring的一个Bean配置。
     传统的DAO设计的话,需要修改一个DAO接口,修改DAO实现。
     传统的DAO设计对于增加或者修改查询接口的话,工作量稍微少于我的设计。
     当系统的表为10个时,传统的DAo设计一般需要产生10个dao类(需要接口的话,为20个),如果一个dao类需要支持复杂的read查询为5个的话,对于查询的method,至少需要50个method实现。
    而我的设计除去Type这个底层稳定构架层次以外,仅仅需要一个DAO实现类,如果尽量采用NamedQuery,那么整体QueryObject类实现一般仅仅需要10个左右即可,QueryObject还有利于抽象复杂的查询,甚至包括需要使用到Criteria的QueryObject实现。比如这么一个抽象:
public class CommonQuery implments QueryObject{
    public Object query(Session session,Set parametes);{
           //验证参数合法性
         if(getValidatable(););
          validate(getQueryCfg();,parameters);;
        		List res = session.createCriteria(parameters.get("type"););
				 .add( Expression.eq(parameters.get("queryParam");,parameters.get("value");););
				 .list();; 
   }
}

他可以抽象对某个表的某个字段等于某值的查询。
当然这些设计需要涉及到一个参数集合规范的问题,这些都是可以在项目初期很快设计明确下来以形成正式的QueryObject设计规范文档。

整体评估:
当应用中的entity数量极大时,这种dao的设计将大大优越于传统的dao设计。
   发表时间:2004-10-10  
好复杂的设计啊, 偶向来都是用一个map <-> criteria转换器搞定的.

http://forum.iteye.com/viewtopic.php?t=7541&highlight=
0 请登录后投票
   发表时间:2004-10-10  
呵呵,今天再想了一下,确实设计得太过复杂,over design.
真正搞的话,也会采用ReadOnly的说法,呵呵,权当一次胡思乱想罢了,这些咚咚都是捉着思路一口气写下来的,谁要是看着累,还望多多海涵。
另附一点,我这个设计主要针对动态添加查询接口,而不用修改dao,可以验证参数的合法性而作的。没有经过实际项目的时间,如果有人想继续了解,我可以做无偿的帮助。
0 请登录后投票
   发表时间:2004-10-10  
Map的缺点是弱类型,  不过偶这个人很懒惰的, 是绝对设计不出Parameter, ParameterCfg, Type这种东西了,  所以就偷懒用Map了.
0 请登录后投票
   发表时间:2004-10-10  
Readonly 写道
Map的缺点是弱类型,  不过偶这个人很懒惰的, 是绝对设计不出Parameter, ParameterCfg, Type这种东西了,  所以就偷懒用Map了.

其实我认为我这个设计最大的好处还是在于提供稳定的dao查询接口。动态增加查询的能力。paramegerCfg可以规范查询参数的设计
0 请登录后投票
   发表时间:2004-10-10  
map也可以达到稳定DAO api的目的, 举个实际的应用例子吧, 偶再来对应着用map来做做看
0 请登录后投票
   发表时间:2004-10-10  
呵呵这样说来,又是一个复杂的设计,呵呵我的over design比较暴露了。只不过多了这个参数检查而已。
0 请登录后投票
   发表时间:2004-10-10  
DAO的接口不应该简化,因为你需要用一个可以表达含义的名字,如果需要变化同一意义下的内部的查询实现,那么DAO本身的名字和参数不需要改变,如果你要改变的是这个方法本身的行为,那么当然需要改变了

至于Paramter和NamedQuery,Hibernate本身已经提供了,为什么还要设计一套,你有没有发现你做
 public class YourBusiness{
...}
    public void doBusiness();{
...}
        Set parameters=new LinkedHashSet();;
          parameters.add("firstName","test");;
          parameters.add(2,"test2");;
         List users=(List);getDao.find("findUserWithFullName",parameters);;

的过程和Hibernate提供的大同小异吗,唯一区别就是你不需要每次都去
getHibernateTemplate();.execute(new HibernateCallback(); 

而已
但这个只需要几个快捷方法而已,例如带1个、2个、3个、4个、5个参数的find方法,这样写起来也很方便,一句话就可以了,可以写在DAO的超类里面,并且不应该作为外部接口暴露出来
0 请登录后投票
   发表时间:2004-10-10  
一开始我做这个设计的思路是想简化dao类的数目,我想通过提供一个统一而稳定的DAO接口来简化整体需要公开的dao类的数目。
不过再细细考虑,我的设计被证明是错误的。因为结果下来,我的设计是减少了公开的dao接口(今需要一个),而增加了内部实现的复杂度,需要增加一个QueryObject,QueryCfg类。还要修改相关的 配置文件(spring的bean配置)。用复杂度换外部调用的简单性,不知道这算不算一个过渡设计。
用map构造复杂查询的语句也未尝不可,正如readonly所说的一样。
0 请登录后投票
   发表时间:2004-10-10  
DAO本身的目的有两个:
1。提供统一的存取层,以便外部框架和机制针对这一层进行特别的处理,或者外部可以提供这一层次上的超类为你服务,你可以在一个明确的层次上重用别人的工作,但这不是专门针对DAO,而是分层结构自动带来的好处。

2。而DAO本身的目的就是为了封装数据存取的差异性,如果你直接用这种方法来做的话,DAO有什么用,根本不需要了
List users=(List);getDao.find("findUserWithFullName",parameters);;

如果DAO改成 JDBC实现,这算什么?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics