Dict.CN 在线词典, 英语学习, 在线翻译

都市淘沙者

荔枝FM Everyone can be host

统计

留言簿(23)

积分与排名

优秀学习网站

友情连接

阅读排行榜

评论排行榜

Tapestry Spring Hibernate (zhuan)

 

Web层的Tapestry负责数据输入输出, 响应用户事件,及输入校验的工作, 通过访问预先加载的WebApplicationContext(由Spring提供, 包含着所有Service bean)获得Service层的Service Bean, 把业务操作都委托给它们.

Service层的bean则负责use case逻辑, domain相关的逻辑委托给domain model中的bean去实现. Service通过DAO完成对domain model的持久化工作. Service负责数据库事务和Hibernate Session的管理(通过Spring的声明式事务管理和与之集成的Hibernate Session管理). Service层的另一项重要工作是权限和访问控制。

Domain model负责表示问题域的数据和domain logic. DAO使用Hibernate持久化数据以及查询. 在实现DAO时, 我们使用了Spring的Hibernate DAO Support,极大地简化了代码, 很多方法都只用简单的一行完成. 有意思的是, 最后完成的HibernateDAO的代码量居然比我写的MockDAO的代码少了一半还多

这样的架构优点很明显, 层次清晰, 各层的职责也明确, 便于分层设计与开发, 结合mock和spring的IOC, unit test也是非常容易的. 而且后台(Service, domain model and DAO)的代码不依赖于Web容器或是EJB容器的API, 移植性非常好, 同样的代码可以在Web app中使用也可在普通的Java app中使用, 只需更换UI层.

按照这个整合的构架,我们实现一个简单的实例,实现了列表分页查询和显示,数据增删改,基于Hibernate Criteria Query提供了一个比较通用的查询机制。利用Middlegen和Velocity我们可以从已经建好的数据库表结构自动生成Hibernate映射文件,实体类和DAO,极大地减少了工作量。我们还对这个小例子进行了压力测试(测试时的数据量为10万条记录),确定平台不存在性能问题。

通过这个实例我们把整个架构基本走通一遍,并总结了使用这套架构开发时适用的开发流程和需要做的工作。

困扰的问题

问题1:要不要使用DTO?
在上面的架构中我们并没有明确Service和Web层间的数据传输是如何进行的。我们讨论好久要不要使用DTO,最后的结论是不用。

使用DTO有两个主要的理由:
1、减少Web层和Service层间的方法调用,通过一个方法调用就将Web需要的数据都传给Web。
2、隔离domain model和Web层。

第一个理由在当前架构下是不成立的。因为我们的架构是集中式的,Web和Service是在同一个JVM中,它们之间的方法调用是没有EJB远程访问的巨大消耗的。
第二个理由还是需要考虑的。如果允许把domain model中的对象传给Web层,那么修改domain model,就会影响到Web层。
如果使用DTO,那么domain model实现上的变化就不会影响到Web。但是大量的变化不是domain model实现上的变化,而是domain model接口的变化,比如一个domain model的对象上添加了一个属性,而这个属性需要用户修改,那么这时候必须修改Web层,不管是不是用了DTO。
而且使用DTO,就需要维护着一大堆对象,或是它们的生成器,这是非常无聊、且容易出错的工作。
基于这些考虑,没有必要使用DTO, 直接把domain object传给Tapestry的web层,利用Tapestry提供强大的数据绑定和组件功能很方便

 

问题2:Entity like domain model or rich domain model?
我们使用Middlegen自动生成Hibernate映射文件,Entity类和DAO类, 但是生成的Entity只含有简单的属性和getter, setter方法。
因此我们遇到了一个问题:我们的domain model还要不要包含domain logic?如果包含,那么和自动生成工具如何结合?
虽然一个rich domain model可以减少Service中的重复代码,提高复用性。 但是如何同自动生成结合?
或许可以使用<meta>标签,生成抽象基类,我们继承这些自动生成的基类,添加业务方法。
但是单纯从结构的分离角度来考虑, domain model不应该包括复杂的domain logic, 只是作为一个data model bean, 再加上一些简单的logic, 比如addChild的同时,设置child的parent此类简单logic
而且在POJO里面塞太多业务逻辑会导致Hibernate产生你预想不到的后果。

 


问题3:Model driven or Data driven?
这其实是一个很无聊的问题. 采用Model driven还是Data driven的方式一直都处于热烈的讨论中。我们主要是受到Rod Johnson(Expert one-on-one J2EE Design and Development 一书的作者) 的影响,采用了Data driven的方式。先作数据库设计,生成库表,然后用Middlegen反向生成Hibernate映射文件和Entity及DAO。
但是在进入项目应用之后发现这种方法有可能会出现两个问题:
a) 数据库设计仅说明了系统要管理的静态数据,我们还是得作面向对象分析,以反映系统的动态行为。特别是当系统的业务不仅仅是简单的CRUD操作时,这个问题更严重。
b) 数据库设计为了优化性能,可能会把好几个应该是单独实体的数据放入一个实体中。这样如果直接把这种极粗粒度实体映射成Entity类,那简直是不可接受的。
面向对象的分析设计模型得到的类都是相当细粒度的。这种情况还得作面向对象的分析,明确到底这个粗粒度的大表应该映射成那几个细粒度的对象。
如果采用Model driven,则可以用AndroMDA生成domain model,Hibernate DAO,Hibernate mapping,数据库表,简单的Service和前台的Tapestry页面。
不过换个角度分析, 采用哪种设计模式只是个人习惯而已,无论是Model driven或者Data driven,对于相同的需求,2者最终的设计应该都是很类似的。
更周全的做法大概可以从2边同时考虑的,一边做Model,一边还要考虑数据库的结构, 再进行相应的调整,用下来也没有什么问题。没有用代码生成,
只是用Hibernate的Eclipse plugin (Hibernate Synchronizer)来帮我做一些code assist. 也可以用xDoclet来生成mapping文件和DDL.

 

问题4:Hibernate Session生命周期如何管理?
对于Hibernate Session的生命周期我们采用的是Session-per-Transaction模式,未采用Open Session in View模式。
虽然Hibernate team认为这种方法没什么不好,而且FreeRoller和Atlassian的confluence都使用了Open Session In View这种模式,但是我们对它可能产生的影响还没有很好的把握,所以暂时弃置不用。
如果Web层要访问lazy load的数据, 需要先调用Service的业务方法, 以获得数据. 不过Open Session in View模式其实并不复杂.


问题5:Use case logic 和 domain logic 如何区分?
Service负责use case logic,domain model负责domain logic。这样的划分看起来很好,实现起来就很麻烦。
如何确定什么是use case logic,什么是domain logic?
不过如果像第二个问题中最后所说, domain model里面没有什么domain logic 的话,这个问题不存在


问题6:Service粒度如何确定?
这个问题真是很烦,原先考虑使用usecase controller的方式,每个usecase对应一个Service,但是发现这样复用性太低,而且好多地方必须复用相同的功能
另一种方法是用package level service,每个package作个service,这样倒是可以重用,但是感觉太死了,不好。
回到domain model里面没有什么domain logic的情况下,Service的功能就切得很细,尽量重用,一个package可能有多个service

现在也没有什么很好的办法,只好在详细设计时根据具体情况确定需要多少个Service了。TBD.

问题7:权限如何设定?如何检查?
权限设定也是个头疼的问题。我们本想是按照use case设定权限,每个用例一个权限。在角色设定的时候直接处理的都是业务意义非常明确的权限。
但是在权限验证过程中发现了问题:如果在Service的方法中验证权限,而且这个方法在多个用例中用到(复用Service),那么这个Service的方法就需要检查多个权限; 如果每个Service方法对应一个权限, 那么权限又太细了, 不像use case权限那样代表一个完整的业务.
一种考虑是将权限控制都做在Service上,权限系统的设计理念来自于Unix操作系统的权限策略,只不过我把文件和文件夹换成了Service,把组换成了Role,此外又加了一些控制对象进去,这样实现下来功能相当的强大,而且很灵活,扩展起来很方便,增加一个Service,只需要在配置文件里面设定该Service的权限级别就行了.
时针对Service的权限是得自己写代码实现的,先是定义一个权限控制接口,然后写了一个抽象类继承该权限接口,把主要的代码实现都写好了,针对特定的Service不同的部分定义为一个抽象方法,其实该抽象方法也就是把类名传递过去而已。这样每个要实施权限控制的Service只要继承该抽象方法,然后在配置文件里面定义自己的权限级别就行了。就算以后再增加别的Service模块,也非常快捷,不需要动现有的代码
如果结合了Spring, 在Spring里面可以使用AOP的办法针对Service增加权限控制,而不需要自己手工写.
看起来手工方式还是没有上面描述的Spring控制方式优雅

 

额外的问题:
从问题5/6中再上升一步, 考虑一下DAO的做法.一种常见的用法是写一个通用的DAO类, 而是整个系统就一个DAO (PersistenceManager), 里面只有create, update, delete entity这3个方法。另外写一个DAO, 专门做查询 (QueryManager), 用hibernate的named query做常用的查询, 用criteria来做基于用户输入的动态查询.
criteria 和 named query其实都是hardcode,domain object attribute name改变的时候,还得记得手工去修改这些代码,unit test很难覆盖到所有的criteria search 和 named query里面的代码.
DAO是为了切换持久层API用的,准确的来说,是当你在使用JDBC来直接访问数据库的时候,由于不同的数据库的sql有差别,所以需要一个DAO来切换不同的数据库的JDBC访问代码。每个数据库的JDBC代码你都要写一套,当切换数据库的时候,就可以通过DAO来切换不同的JDBC代码实现了.
是当我们使用Hibernate的时候,除了个别情况,大部分时候是不需要考虑跨数据库问题的,Hibernate已经做的足够好了。个别情况下的代码我们也可以单独做为特例处理,因此我想不到DAO的必要性了。
如果说使用DAO是为了将来切换别的O/R Mapping的话,我觉得这个话是不现实的,毕竟每种O/R Mapping的实现方式差异还是很大的,这直接影响到整个持久层设计.
而当业务层代码是直接调用Hibernate API来操纵PO的,持久层就只剩下了简单的POJO了.
另外一种常见用法是而在业务层针对每个POJO,写一个对他的CRUD操作的Manager类. 可以参看一下Appfuse的做法.其实这等于是在完成DAOImpl应该完成的工作,只不过现在把他归到了业务层来说罢了。然后你就可以根据你的业务逻辑来写你的业务Service了
在这里,至于如何切换业务逻辑的颗粒度问题, 基于OOAD的原则,应该把无关的业务放在不同的class里面,降低类之间的耦合性,提高类的可重用度。那么根据这个原则如何切分就是一个清楚的事情了.


(以上文字整理自洛宾,QW等N人在爪哇爱死论坛上的讨论)

posted on 2006-02-26 10:33 都市淘沙者 阅读(493) 评论(0)  编辑  收藏 所属分类: Spring+Struts+Hibernate


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


网站导航: