首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Rapae 减杀DAO的一种方法

2012-10-24 
Rapae 弱化DAO的一种方法??????? 可怜的DAO层已经被各位大侠蹂躏得体肤完肤了,从范型DAO一直被蹂躏到现在

Rapae 弱化DAO的一种方法

??????? 可怜的DAO层已经被各位大侠蹂躏得体肤完肤了,从范型DAO一直被蹂躏到现在只剩下一个可怜巴巴的接口,无不体现Java人追求敏捷开发的热情。其实,DAO层本来的作用就应该自从Hibernate一类优秀的ORM框架诞生之日起就应该消失灭迹了的。既然如此,那么我们就毁灭得更彻底一点。

?

下面是我对Service与DAO层整合的一些构想

?

约定优先于配置
一、Rapae代理接口标签定义

query属性:查询语句或者命名查询名称
    不止由一个单词组成时:简单查询语句由单个单词组成时:为命名查询,名称规则为[query的值]为默认值""时:为命名查询,名称规则为[类名.方法名]
count属性:用于统计个数的查询语句或者命名查询名称,固定返回为一个Long型的数据
    不止由一个单词组成时:简单查询语句由单个单词组成时:为民命查询,名称规则为[count的值]为默认值""时:自动生成count(*)语句
    query为语句查询时:count自动生成为语句查询,规则为:select count(*) + query从第一个from开始到语句结束query为命名查询时:count自动生成为命名查询,规则为:[query命名查询名称_count]
PS:当接口的方法没有Rapae注释的时候,按query与count均属于默认值情况处理








二、Pagination翻页注释

注释定义:
    FirstResult注释:方法级上的Annotation,它标记了分页查询时所要知道的第一条记录所在的位置。期待的类型为intMaxSize注释:方法级上的Annotation,它标记了分页查询时所要知道的每页最大查询记录数。期待的类型为intTotal注释:方法级上的Annotation,它标记了分页查询后返回的总数统计。期待的类型为longResult注释:方法级上的Annotation,它标记了分页查询后返回的结果。期待的类型为java.util.Collection<E>
详细说明:
    分页查询时:第一个参数必须是能同时提供有FirstResult和MaxSize注释方法的类,并且方法期待的返回类型都必须匹配,否则将会抛出异常。Rapae通过调用被标注的方法来进行分页查询。分页查询返回时:返回的类必须同时提供有Result和Total注释的方法,参数个数为1,并且期待的类型都必须匹配,否则将会抛出异常。与此同时,若返回的类同时还能提供FirstResult、MaxSize注释的方法,方法的参数个数为1且为期待类型,那么Rapae在分页查询完成后将查询用到的FirstResult、MaxSize值原封不动的通过标注的方法设回给返回类。






三、CRUD基本查询注释

注释定义:
    Create注释:格式 T [方法名] (T t);Read注释:格式 T [方法名] (java.io.Serializable pk);Update注释:格式 T [方法名] (T t);Delete注释:格式 T [方法名] (java.io.Serializable pk);
一旦被标注上了CRUD标签,则必须严格遵循标签所规定的格式,否则抛出异常。






四、查询行为方式与方法的返回类型、参数类型、参数个数以及方法名称之间的约定

查询行为方式:
    ?
      执行查询
      返回值必须为void查询多条记录(不翻页)
      返回值是java.util.Collection<E>的实现
      查询多条记录(翻页)
      返回类必须同时提供标注有Result与Total的方法,并且参数个数为1,参数类型为标签所期待的类型返回类可以选择性的提供标注有FirstResult和MaxSize的方法,并且参数个数为1,参数类型为标签所期待的类型传入的第一个参数必须同时提供标注有FirstResult与MaxSize的方法,参数个数为0,返回类型为标签所期待的类型查询单条记录
      返回值必须是一个对象
查询条件传递:
    若是翻页查询,则参数个数至少为一个,且第一个参数必须为提供标标注有FirstResult与MaxSize的方法。若查询条件是通过可变参或者Collection集合类进行传递的,则按顺序对查询条件进行设置若查询条件是通过参数列表直接传递进来的,则按参数列表定义的顺序对查询条件进行设置若查询条件是通过Map传递进来的,则可通过Map对参数的参数进行设置
save(user);sendMail(user.getEmail());
这样的代码完全可以写在泛型DAO中达到DAO和Service合并的目的。
而采用楼主这样的注解形式的简化方法~碰到这样的问题,没有办法了。只能引入Service~~这不是一种简化了,并没有达到二合一的目的。只是让本来可以看起来“富”的DAO变成了一个空壳子,以前的Service还是Service。DAO不能进行任何的修改,如果修改就必须引入Service~这样的DAO是那么的“瘦”以至于变成了一种看着蹩脚的代码(嗯,那一堆一堆的getter、setter就是这样的~) 23 楼 fireflyc 2008-06-17   我的认为这种简化方法不如泛型DAO直接有效。
和泛型DAO比较,问题归结在使用注解还是代码上

我在xml和注解之间的归结是:
如果是配置的内容就是用配置文件xml,
如果是代码的一部分而不是配置就是用注解。

现在是代码和注解的归结:
如果能使用代码描述优先使用代码描述,这样才合乎逻辑,
如果需要描述的信息是对代码的描述那么就使用注解。 24 楼 aninfeel 2008-06-18   感觉没有简直这回事,少了一个DAO只是把代码移到别处而已,开始可能会因为新鲜感而感到很方便,但是久了可能就会怀念以前的。其实各种构架没有纯粹的好和坏,只是某些长处和短处相互转换而已。 25 楼 vlinux 2008-06-18   <p>刚刚学院开毕业生会回来,昏昏欲睡,不过想清楚了一些问题。</p>
<p>?</p>
<p>这种构想的技术还是多在实战中用用比较好,所以我昨天晚上用来帮我的同学的毕业设计写了一个留言板,或许是我自己比较了解自己写的代码的缘故吧,觉得用Rapae还挺快...<img src='../../../../../images/smiles/icon_biggrin.gif' alt=''/></p>
<p>?</p>
<p>写的过程中也总结出一些使用的技巧与规则。Service层严格避免相互调用,如果必须要自己实现的方法需要操纵数据库的,都统一通过ORM框架来完成。至于</p>
<div class='quote_title'>downpour 写道</div>
<div class='quote_div'>这样你就会发现,当你的Service层和Dao层合并以后,会造成你的业务逻辑层代码有多臃肿。我一直很困惑,如果一个业务逻辑方法中要包含5,6次的复杂数据库操作,你如果不搞个Dao,你的Service层代码很可能会瞬间超过1000行。 </div>
<p>?所说的问题。我认为是简单问题复杂化了。现在的ORM框架操纵数据库都非常简洁。基本上很少出现超过1000行的代码,如果真的出现了,那真的也是这条业务非常繁琐了。</p>
<p>?</p>
<div class='quote_title'>roy042 写道</div>
<div class='quote_div'>结合自己经历过的一个项目的体验,我觉得比较合适的做法是DAO层使用接口,DAO的实现类只封装数据库操作,而在Service层中处理业务逻辑。DAO层之间不相互调用,而由Spring向Service中注入要使用的DAO。 <br/>...<br/>而如果DepartmentManager要操纵User,则注入UserManagerImpl,而并不是注入UserDAOImpl。
<br/></div>
<p>?这样的做法以前我也迷茫过,不过后来实践中我发现一个问题:如果DepartmentManager需要操纵User做UserManager接口没有定义的业务,那这个模型要实现起来就必须要为UserManager暴露出很多没有必要的public method;而且将会束缚UserManagerImpl类的维护人员:他们不仅要测试UserManagerImpl,还得测试使用了这个接口的所有Manager!</p>
<p>?</p>
<p>虽然我在前面曾经说过:</p>
<div class='quote_title'>vlinux 写道</div>
<div class='quote_div'>如图2所示。Service层不再相互独立,相互的调用使得整个Service层紧密的耦合在了一起。我不得不承认修改一处业务逻辑的方法非常很有可能会导致其他业务模块的改变。但是这样的确加快了我的开发效率。</div>
<p>?但是同时也认为这是一种不好的行为。</p>
<p>?</p>
<p>?</p>
<p>也正如 aninfeel 所说,我也越来越觉得这更像是一种玩具了。只不过之前我们是这么玩,现在腻烦了换个花样玩,而且这个花样还不一定好玩罢了。</p>
<div class='quote_title'>aninfeel 写道</div>
<div class='quote_div'>感觉没有简直这回事,少了一个DAO只是把代码移到别处而已,开始可能会因为新鲜感而感到很方便,但是久了可能就会怀念以前的。其实各种构架没有纯粹的好和坏,只是某些长处和短处相互转换而已。 </div>
<p>?</p>
<p>?</p>
<p>感谢和我一起讨论的所有javaeyer,让我看到了不足。感谢浏览过这个帖子的网友。同时我也觉得,我作为一个毕业生,参加的实际项目实在是太少了。要做出一个不像玩具的玩具,还是得经受更多的磨炼。当然,我不是想将这些代码变成一个框架,现在大家所学的框架已经够多的了,而且我也没这个资格。我只是希望能和大家更多的分享从 构想 -&gt; 设计 -&gt; 实现 -&gt; 应用 的快乐。谢谢大家的支持。</p>
<p>?</p>
<p>顺便B4一下我的学院,为啥毕业论文的格式要求比Compiler还严,我已经重新打过不下5次了,气愤。</p>
<p>?</p> 26 楼 icewubin 2008-06-18   roy042 写道结合自己经历过的一个项目的体验,我觉得比较合适的做法是DAO层使用接口,DAO的实现类只封装数据库操作,而在Service层中处理业务逻辑。DAO层之间不相互调用,而由Spring向Service中注入要使用的DAO。
比如:
UserDAO,UserDAOImpl,UserManager,UserManagerImpl;
DepartmentDAO,DepartmentDAOImpl,DepartmentManager,DepartmentManagerImpl;
其中UserManagerImpl中注入UserDAO的实现类UserDAOImpl,
DepartmentManagerImpl中注入DepartmentDAO的实现类DepartmentDAOImpl,
而如果DepartmentManager要操纵User,则注入UserManagerImpl,而并不是注入UserDAOImpl。
这样可以将业务逻辑与具体的数据读写操作剥离。



1.没那么容易剥离,完全剥离的话很容易产生性能问题。

2.既然orm已经封装了基础crud,就在dao里写业务逻辑好了,单表的业务逻辑,没什么不好。
  强调一点,业务逻辑CRUD不是基础CRUD,例如新增人员时,年龄小于5岁或者来自于日本的都不让注册或者数据库里已经有同名的用户名了,都抛出异常。这种代码就是业务逻辑,写在dao里面再合适不过了。
  多表的放到ServiceImpl里去。 27 楼 Frederick 2008-06-19   对于简单的操作,比如那个getCustomer(),用范型的DAO足够了。没看出你的改动简化在那里,还会带来其他一些问题,比如测试业务方式的时候,很难替换持久化层。
对于业务复杂的操作,DAO和Service整合到一起以后,会导致业务方法实在太复杂了。
在我涉及到的领域(电信的信息系统和GIS系统),你的所谓弱化毫无价值。在其他领域的情况我不了解,不好说。

个人感觉帖主在有了一定的商业项目实践经验以后再来看这个问题,可能会有截然不同的看法。 28 楼 vlinux 2008-06-19   <div class='quote_title'>Frederick 写道</div>
<div class='quote_div'>测试业务方式的时候,很难替换持久化层。 </div>
<p>?这个我写TestCase的时候遇到了,确实不能模拟DAO单独测试Service,只能在@Before、@BeforeClass的时候创建测试用的数据,然后再在@After、@AfterClass中进行清理。</p>
<p>?</p>
<p>写完这个帖子至少也让我明白Service与DAO层在项目中还是各自有各自的用途,谢谢。我现在也不再拿它来简化我在工作中遇到的Service,只是退一步简化DAO而已。</p>
<p>不过我打算会为以后接私活的时候考虑用来替换Service,毕竟那些小项目的业务逻辑都非常简单。</p> 29 楼 liubaoshan 2008-06-20   roy042 写道结合自己经历过的一个项目的体验,我觉得比较合适的做法是DAO层使用接口,DAO的实现类只封装数据库操作,而在Service层中处理业务逻辑。DAO层之间不相互调用,而由Spring向Service中注入要使用的DAO。
比如:
UserDAO,UserDAOImpl,UserManager,UserManagerImpl;
DepartmentDAO,DepartmentDAOImpl,DepartmentManager,DepartmentManagerImpl;
其中UserManagerImpl中注入UserDAO的实现类UserDAOImpl,
DepartmentManagerImpl中注入DepartmentDAO的实现类DepartmentDAOImpl,
而如果DepartmentManager要操纵User,则注入UserManagerImpl,而并不是注入UserDAOImpl。
这样可以将业务逻辑与具体的数据读写操作剥离。


我也在一直是用这种做法,感觉比较灵活,直观层次分明. 30 楼 icewubin 2008-06-26   liubaoshan 写道roy042 写道结合自己经历过的一个项目的体验,我觉得比较合适的做法是DAO层使用接口,DAO的实现类只封装数据库操作,而在Service层中处理业务逻辑。DAO层之间不相互调用,而由Spring向Service中注入要使用的DAO。
比如:
UserDAO,UserDAOImpl,UserManager,UserManagerImpl;
DepartmentDAO,DepartmentDAOImpl,DepartmentManager,DepartmentManagerImpl;
其中UserManagerImpl中注入UserDAO的实现类UserDAOImpl,
DepartmentManagerImpl中注入DepartmentDAO的实现类DepartmentDAOImpl,
而如果DepartmentManager要操纵User,则注入UserManagerImpl,而并不是注入UserDAOImpl。
这样可以将业务逻辑与具体的数据读写操作剥离。


我也在一直是用这种做法,感觉比较灵活,直观层次分明.

正好热点中另有一个循环依赖的问题,DepartmentManager和UserManager不宜互相依赖。
http://www.iteye.com/topic/205271
针对这个例子把我的建议再阐述一下:

1.DAO和Manager合并,manager中的单表业务应该是被复用的。

2.上面说到复用,被谁复用呢,被service复用。只能在service中注入manager。

3.你可以说,你的DAO相当于我说的Manager,你说的Manager相当于我说的service,不一样,我认为一般情况,可以约定不允许在service中注入service。“只能在service中注入manager”。
31 楼 vlinux 2008-06-26   <p>Service层应该避免相互感知,DAO层也应该避免相互感知。这样可以避免不必要的循环依赖,而且还可以避免更隐蔽点的循环调用。</p>
<div class='quote_title'>icewubin 写道</div>
<div class='quote_div'>我认为一般情况,可以约定不允许在service中注入service。“只能在service中注入manager”。 </div>
<p>?我赞同这种做法,而且也方便进行测试。</p> 32 楼 zhu_chen001 2008-06-26   @Transactional 
public abstract class AccountServiceImpl extends JpaDaoSupport implements AccountService {  
      
    public Account login(String username, String password) {  
        List<?> result = getJpaTemplate().find("from Account where username = ? and password = ?", username, password);  
        return (Account) (result.isEmpty()?null:result.get(0));  
    }  
 
}

一看就是一个DAO的翻版,真正的逻辑层应该屏蔽sql的出现,从而使函数共用达到最高,所以dao可以简化,但是不会消失,否则也只是把dao和manager混合,反而没有达到效果 33 楼 vlinux 2008-06-26   EQL、HQL的出现就是屏蔽了具体的SQL。

DAO也许不会消失,但是绝对不是这个理由 34 楼 icewubin 2008-06-26   zhu_chen001 写道@Transactional 
public abstract class AccountServiceImpl extends JpaDaoSupport implements AccountService {  
      
    public Account login(String username, String password) {  
        List<?> result = getJpaTemplate().find("from Account where username = ? and password = ?", username, password);  
        return (Account) (result.isEmpty()?null:result.get(0));  
    }  
 
}

一看就是一个DAO的翻版,真正的逻辑层应该屏蔽sql的出现,从而使函数共用达到最高,所以dao可以简化,但是不会消失,否则也只是把dao和manager混合,反而没有达到效果

所以我建议dao和单表操作的manager混合,就是避开你说的这个问题。

但是我还是要说,dao不可能屏蔽sql(包括hql、qbc、qbe),也没有必要屏蔽。
完全屏蔽在稍微复杂一点的系统中,由于团队中水平的高低,绝对会带来性能问题,而且还不好查,不管是orm还是其他方式实现DAO,都需要灵活控制sql或者是sql语句的生成。

热点排行