锁定老帖子 主题:一次关于简化DAO设计的初步思考!
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2004-10-10
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设计。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2004-10-10
好复杂的设计啊, 偶向来都是用一个map <-> criteria转换器搞定的.
http://forum.iteye.com/viewtopic.php?t=7541&highlight= |
|
返回顶楼 | |
发表时间:2004-10-10
呵呵,今天再想了一下,确实设计得太过复杂,over design.
真正搞的话,也会采用ReadOnly的说法,呵呵,权当一次胡思乱想罢了,这些咚咚都是捉着思路一口气写下来的,谁要是看着累,还望多多海涵。 另附一点,我这个设计主要针对动态添加查询接口,而不用修改dao,可以验证参数的合法性而作的。没有经过实际项目的时间,如果有人想继续了解,我可以做无偿的帮助。 |
|
返回顶楼 | |
发表时间:2004-10-10
Map的缺点是弱类型, 不过偶这个人很懒惰的, 是绝对设计不出Parameter, ParameterCfg, Type这种东西了, 所以就偷懒用Map了.
|
|
返回顶楼 | |
发表时间:2004-10-10
Readonly 写道 Map的缺点是弱类型, 不过偶这个人很懒惰的, 是绝对设计不出Parameter, ParameterCfg, Type这种东西了, 所以就偷懒用Map了.
其实我认为我这个设计最大的好处还是在于提供稳定的dao查询接口。动态增加查询的能力。paramegerCfg可以规范查询参数的设计 |
|
返回顶楼 | |
发表时间:2004-10-10
map也可以达到稳定DAO api的目的, 举个实际的应用例子吧, 偶再来对应着用map来做做看
|
|
返回顶楼 | |
发表时间:2004-10-10
呵呵这样说来,又是一个复杂的设计,呵呵我的over design比较暴露了。只不过多了这个参数检查而已。
|
|
返回顶楼 | |
发表时间: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的超类里面,并且不应该作为外部接口暴露出来 |
|
返回顶楼 | |
发表时间:2004-10-10
一开始我做这个设计的思路是想简化dao类的数目,我想通过提供一个统一而稳定的DAO接口来简化整体需要公开的dao类的数目。
不过再细细考虑,我的设计被证明是错误的。因为结果下来,我的设计是减少了公开的dao接口(今需要一个),而增加了内部实现的复杂度,需要增加一个QueryObject,QueryCfg类。还要修改相关的 配置文件(spring的bean配置)。用复杂度换外部调用的简单性,不知道这算不算一个过渡设计。 用map构造复杂查询的语句也未尝不可,正如readonly所说的一样。 |
|
返回顶楼 | |
发表时间:2004-10-10
DAO本身的目的有两个:
1。提供统一的存取层,以便外部框架和机制针对这一层进行特别的处理,或者外部可以提供这一层次上的超类为你服务,你可以在一个明确的层次上重用别人的工作,但这不是专门针对DAO,而是分层结构自动带来的好处。 2。而DAO本身的目的就是为了封装数据存取的差异性,如果你直接用这种方法来做的话,DAO有什么用,根本不需要了 List users=(List);getDao.find("findUserWithFullName",parameters);; 如果DAO改成 JDBC实现,这算什么? |
|
返回顶楼 | |