关于Service和DAO层的功能划分的迷惑 (转载)
原文地址:http://jamesby.iteye.com/blog/54260
Service 层的代码
DAO 层的代码
迷惑的是在上面的代码中,Service 并没有什么业务逻辑,似乎可有可无,是否上面的代码可以将Service层省略,直接在DAO层操作和事务拦截,只有涉及到具体业务逻辑的时候使用Service,当然这会造成Controller和DAO层之间的紧耦合,大家在项目中遇到如上情况都是如何做的?
- public class GeneralService extends AbstractService {
- private GeneralDao generalDao;
- public void setGeneralDao(GeneralDao generalDao) {
- this.generalDao = generalDao;
- }
- public void saveResourceSale(ResourceItemFormbean resourceItem)
- throws Exception {
- generalDao.saveResourceSale(resourceItem);
- }
- public List selectTodayResource(Map params) throws Exception {
- return generalDao.selectTodayResource(params);
- }
- public List selectTdPrintInfo(Integer ddid, Integer linkmanid)
- throws Exception {
- return generalDao.selectTdPrintInfo(ddid, linkmanid);
- }
- public List selectTdPrintItems(Integer ddid) throws Exception {
- return generalDao.selectTdPrintItems(ddid);
- }
- public List selectTodayBuy(Map params) throws Exception {
- return generalDao.selectTodayBuy(params);
- }
- public List selectStockKgzy(Integer ptid, Integer prodid, Integer cmdeptid,Integer page)
- throws Exception {
- return generalDao.selectStockKgzy(ptid, prodid, cmdeptid,page);
- }
- public List selectStockXqzy(Integer ptid, Integer prodid, Integer cmdeptid,
- Integer page) throws Exception {
- return generalDao.selectStockXqzy(ptid, prodid, cmdeptid, page);
- }
- public void deletePublishedResource(Integer pkid) throws Exception {
- generalDao.deletePublishedResource(pkid);
- }
- public void saveXqResourcePublish(XqZyfbModel data) throws Exception {
- generalDao.saveXqResourcePublish(data);
- }
- public XqZyfbModel selectXqResourceDetail(Integer pkid) throws Exception {
- return generalDao.selectXqResourceDetail(pkid);
- }
- }
public class GeneralService extends AbstractService { private GeneralDao generalDao; public void setGeneralDao(GeneralDao generalDao) { this.generalDao = generalDao; } public void saveResourceSale(ResourceItemFormbean resourceItem) throws Exception { generalDao.saveResourceSale(resourceItem); } public List selectTodayResource(Map params) throws Exception { return generalDao.selectTodayResource(params); } public List selectTdPrintInfo(Integer ddid, Integer linkmanid) throws Exception { return generalDao.selectTdPrintInfo(ddid, linkmanid); } public List selectTdPrintItems(Integer ddid) throws Exception { return generalDao.selectTdPrintItems(ddid); } public List selectTodayBuy(Map params) throws Exception { return generalDao.selectTodayBuy(params); } public List selectStockKgzy(Integer ptid, Integer prodid, Integer cmdeptid,Integer page) throws Exception { return generalDao.selectStockKgzy(ptid, prodid, cmdeptid,page); } public List selectStockXqzy(Integer ptid, Integer prodid, Integer cmdeptid, Integer page) throws Exception { return generalDao.selectStockXqzy(ptid, prodid, cmdeptid, page); } public void deletePublishedResource(Integer pkid) throws Exception { generalDao.deletePublishedResource(pkid); } public void saveXqResourcePublish(XqZyfbModel data) throws Exception { generalDao.saveXqResourcePublish(data); } public XqZyfbModel selectXqResourceDetail(Integer pkid) throws Exception { return generalDao.selectXqResourceDetail(pkid); } }
DAO 层的代码
- public class GeneralDao extends AbstractDao {
- public void saveResourcePublish(ZyfbModel data) throws Exception {
- getSqlMapClientTemplate().update("MainProcedure.saveResourcePublish",
- data);
- }
- public void updateResourcePublish(ZyfbModel data) throws Exception {
- getSqlMapClientTemplate().update("MainProcedure.updateResourcePublish",
- data);
- }
- public void deletePublishedResource(Integer pkid) throws Exception {
- getSqlMapClientTemplate().update("MainProcedure.deletePublishedResource",
- pkid);
- }
- public List selectStockXqzy(Integer ptid, Integer prodid, Integer cmdeptid,
- Integer page) throws Exception {
- Map params = new HashMap();
- params.put("ptid", ptid);
- params.put("prodid", prodid);
- params.put("cmdeptid", cmdeptid);
- params.put("row", new Integer(15));
- params.put("page", page);
- return getSqlMapClientTemplate().queryForList(
- "MainProcedure.selectStockXqzy", params);
- }
- public void saveXqResourcePublish(XqZyfbModel data) throws Exception {
- getSqlMapClientTemplate().update("MainProcedure.saveXqResourcePublish",
- data);
- }
- public XqZyfbModel selectXqResourceDetail(Integer pkid) throws Exception {
- return (XqZyfbModel) getSqlMapClientTemplate().queryForObject(
- "MainProcedure.selectXqResourceDetail", pkid);
- }
- }
public class GeneralDao extends AbstractDao { public void saveResourcePublish(ZyfbModel data) throws Exception { getSqlMapClientTemplate().update("MainProcedure.saveResourcePublish", data); } public void updateResourcePublish(ZyfbModel data) throws Exception { getSqlMapClientTemplate().update("MainProcedure.updateResourcePublish", data); } public void deletePublishedResource(Integer pkid) throws Exception { getSqlMapClientTemplate().update("MainProcedure.deletePublishedResource", pkid); } public List selectStockXqzy(Integer ptid, Integer prodid, Integer cmdeptid, Integer page) throws Exception { Map params = new HashMap(); params.put("ptid", ptid); params.put("prodid", prodid); params.put("cmdeptid", cmdeptid); params.put("row", new Integer(15)); params.put("page", page); return getSqlMapClientTemplate().queryForList( "MainProcedure.selectStockXqzy", params); } public void saveXqResourcePublish(XqZyfbModel data) throws Exception { getSqlMapClientTemplate().update("MainProcedure.saveXqResourcePublish", data); } public XqZyfbModel selectXqResourceDetail(Integer pkid) throws Exception { return (XqZyfbModel) getSqlMapClientTemplate().queryForObject( "MainProcedure.selectXqResourceDetail", pkid); } }
迷惑的是在上面的代码中,Service 并没有什么业务逻辑,似乎可有可无,是否上面的代码可以将Service层省略,直接在DAO层操作和事务拦截,只有涉及到具体业务逻辑的时候使用Service,当然这会造成Controller和DAO层之间的紧耦合,大家在项目中遇到如上情况都是如何做的?
将DaoHelper注入到AbstractService 中,然后针对DaoHelper的每一个方法调用在AbstractService 进行代理,也就是说所有的操作都需要通过Service不可以绕行,而且这样也不需要Service继承Dao,Service继承Dao总是感觉很奇怪。
当然缺点就是对于每一个Service都需要注入这样一个DaoHelper。
我记得这个问题在阎宏在《java与模式》曾经讲过:要尽量使用合成/聚合,尽量不使用继承。
而在我们这个问题上刚好是最佳注脚
Dao跟service本身就不是在一个层次上 让他们继承实在有些牵强,而采用合成/聚合则使dao层和service层有更松散的耦合,所以就耦合性来讲 采用合成聚合方式更为合理。
但是采用合成聚合方式会在xxxService类中多出一个daoHelper对象,好像是增加了复杂度。
其实只要明白了这个道理就行了,怎么简单怎么来啦
将DaoHelper注入到AbstractService 中,然后针对DaoHelper的每一个方法调用在AbstractService 进行代理,也就是说所有的操作都需要通过Service不可以绕行,而且这样也不需要Service继承Dao,Service继承Dao总是感觉很奇怪。
当然缺点就是对于每一个Service都需要注入这样一个DaoHelper。
然后BaseService这样
省略Dao层!
同意这种方式
我曾经在项目中这样用过
第一种:BaseService不需要继承BaseDao
各种query,update等操作在Service和Dao中都做声明。Service不需要同BaseDao偶合过紧密,这也是第一页代码的实现方式。
第二种:BaseService不需要继承BaseDao,退化使用Dao层
各种query,update等操作在Service做声明,在Dao层不作声明,既Service直接调用BaseDao的queryForList,queryForObject等方法,由Service确定statementid。
第三种:BaseService需要继承BaseDao,退化使用Service和Dao
各种query,update等无业务逻辑的操作既不在Service中声明,也不在Dao中声明,Controller直接构造参数,确定statementid,然后调用BaseService的queryForList,queryForObject等方法.
本来采用第一种方式实现,发现代码过于繁琐;
后采用第二种,发现仍有简化空间
目前采用第三种,Service和Dao中几乎不用写任何代码,实现为第一位,项目时间紧。
AbstractDao 为什么要 extends 一个 SqlMapClientDaoSupport ?
SqlMapClientDaoSupport是什么?
看代码还是太detail,没有办法overview,虽然从直觉上我觉得你现在这样做有些复杂了,但是还没有充分的直接的理由下结论。能否把架构图展示一下先?
SqlMapClientDaoSupport 是 org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
我的代码很复杂吗?可能我没有表述清楚.
所有的Service继承AbstractService,
所有的Dao继承AbstractDao,
CRUD的简单操作不用在Service和Dao层定义,简单的Query直接写在Service中.
复杂的逻辑如isChecked(),count等直接和业务逻辑相关又不是简单的CRUD操作,才放置在DAO中.
AbstractDao 为什么要 extends 一个 SqlMapClientDaoSupport ?
SqlMapClientDaoSupport是什么?
看代码还是太detail,没有办法overview,虽然从直觉上我觉得你现在这样做有些复杂了,但是还没有充分的直接的理由下结论。能否把架构图展示一下先?
然后BaseService这样
省略Dao层!
对于你的疑问,我的看法是(摘自原文):
分层架构:分层架构有助于将大系统分解成子任务,每个子任务限制在一个特定的层次上,层间从上到下依赖,从下到上是松耦合。TCP/IP分层模型是它的最好证明。正如OSI 7层模型的实用模型是TCP/IP 4层模型。MiniFramework中,用户接触到的是两层:表示层和业务层,是J2EE和.NET架构的一种折中。在用MiniFramework开发时,持久层被封装起来,成为一些简单的Helper类,不是独立的一层。
之所以我去掉了持久层,只保留的dao helper,是因为,我发现业务比较简单的系统,往往开发人员喜欢在持久层里面写所有的业务,service层只是dao的一个delegate,非常 thin。要是这样,我就干脆去掉一层算了,这便是dao helper的由来。
严格的分层,可以让表示层的UI用Dreamweaver开发,业务层容器外测试,实现敏捷开发。
接口:在分层模型中,一般都非常强调接口,因为可以让上层只依赖于下层的接口,而不是实现,这样实现可以任意替换和变更,在网络开发,特别是和协议打交道的时候,我们会体会到这种设计的优雅。另外,在组件式开发中,我们也倾向于提供接口。
但是,应用MiniFramework,我们倾向于纵向开发,也就是说,某个模块从表示层到持久层都是一人开发,而不是横向:表示层的去调用业务层开发人员的业务层。这时候,纵向开发就不太适合用接口了,因为接口规范完全由本人把握。这时接口很可能只会带来臃肿,和难维护,一点改动往往牵一动百。当然,在某些情况,如开发Mock测试,Web Services,接口还是很有必要。另外,有人说,我用接口可以实现任意层替换啊?譬如我现在用Hibernate,以后换成IBatis。这完全是一个谎言,至少我看到的大多数应用,将Hibernate换成IBatis的成本绝不亚于重新开发,因为耦合太大。另外,我有个疑问:需要更换的可能性有 1%吗?
个人觉得,接口比较适合于系统软件开发,而不是商业软件开发;而抽象类的继承机制比较适合商业软件开发,而不是系统软件开发。为了复用,系统软件开发倾向于组合,而不是继承。
你有没有想过,写 一些简单的业务逻辑是可以放在DAO里,但要是整个系统很大,业务逻辑很复杂,你会怎么处理呢,我现在在做交行的基金托管项目,业务出奇的复杂,涉及了个方面的知识,有财务会计,证券,股票,基金等等,我觉得还是分开来比较好!!
因为service层已经对CRUD进行了很好的封装了阿,复杂的业务逻辑为什么不可以?CRUD有了,复杂的逻辑需要的原子操作已经有了阿.
如果系统业务简单
可以抽象出通用的dao层(也就一个类)
让业务层继承通用的dao
看了大家的讨论,我现在的想法是干脆将DAO层舍弃,直接由Service来承担,这样最简单.
为什么不考虑这里用泛型?
范型意味着多个method的声明,那用不用范型没有区别.
对于你的疑问,我的看法是(摘自原文):
分层架构:分层架构有助于将大系统分解成子任务,每个子任务限制在一个特定的层次上,层间从上到下依赖,从下到上是松耦合。TCP/IP分层模型是它的最好证明。正如OSI 7层模型的实用模型是TCP/IP 4层模型。MiniFramework中,用户接触到的是两层:表示层和业务层,是J2EE和.NET架构的一种折中。在用MiniFramework开发时,持久层被封装起来,成为一些简单的Helper类,不是独立的一层。
之所以我去掉了持久层,只保留的dao helper,是因为,我发现业务比较简单的系统,往往开发人员喜欢在持久层里面写所有的业务,service层只是dao的一个delegate,非常 thin。要是这样,我就干脆去掉一层算了,这便是dao helper的由来。
严格的分层,可以让表示层的UI用Dreamweaver开发,业务层容器外测试,实现敏捷开发。
接口:在分层模型中,一般都非常强调接口,因为可以让上层只依赖于下层的接口,而不是实现,这样实现可以任意替换和变更,在网络开发,特别是和协议打交道的时候,我们会体会到这种设计的优雅。另外,在组件式开发中,我们也倾向于提供接口。
但是,应用MiniFramework,我们倾向于纵向开发,也就是说,某个模块从表示层到持久层都是一人开发,而不是横向:表示层的去调用业务层开发人员的业务层。这时候,纵向开发就不太适合用接口了,因为接口规范完全由本人把握。这时接口很可能只会带来臃肿,和难维护,一点改动往往牵一动百。当然,在某些情况,如开发Mock测试,Web Services,接口还是很有必要。另外,有人说,我用接口可以实现任意层替换啊?譬如我现在用Hibernate,以后换成IBatis。这完全是一个谎言,至少我看到的大多数应用,将Hibernate换成IBatis的成本绝不亚于重新开发,因为耦合太大。另外,我有个疑问:需要更换的可能性有 1%吗?
个人觉得,接口比较适合于系统软件开发,而不是商业软件开发;而抽象类的继承机制比较适合商业软件开发,而不是系统软件开发。为了复用,系统软件开发倾向于组合,而不是继承。
可以抽象出通用的dao层(也就一个类)
让业务层继承通用的dao
为什么不考虑这里用泛型?
过度设计!
真遇到这种变态需求变化只改变service层就行了??
例如:原来有个客户保存的dao方法,被一个service方法调用,同时这个dao方法直接暴露给前台,这时如果直接暴露的方法要预先做个判断客户的某个条件,而原先引用这个dao的service方法不需要这个逻辑,难道直接修改这个dao方法吗?
难道不能按照需要增加个中间层么?
系统开始可能觉的这样比较烦人,但是要有长远的考虑。
先跑起来,而且要简单
例如:原来有个客户保存的dao方法,被一个service方法调用,同时这个dao方法直接暴露给前台,这时如果直接暴露的方法要预先做个判断客户的某个条件,而原先引用这个dao的service方法不需要这个逻辑,难道直接修改这个dao方法吗?
系统开始可能觉的这样比较烦人,但是要有长远的考虑。
将如上代码放入DAO的超类中,这样我每一个DAO就不需要重复写这些功能雷同的代码!不知道有什么不合适之处.
无论怎么简化dao层,dao对外的接口是不变的,最终业务层还是要是调用dao层的方法,还是会出现楼主的问题
我觉得既然项目业务比较简单,省掉业务层也未尝不可, 在web层直接调用dao 。
如果遇到业务操作就写在dao层,或者web层。
我在 DAO 层所简化的也只是在DAO层把最基本的crud操作简单封装了一下,避免在所有的dao中进行crud的编写!
看来这是一个在简单和规范之间进行的选择,要简单,直接dao,要规范,加层service.
将如上代码放入DAO的超类中,这样我每一个DAO就不需要重复写这些功能雷同的代码!不知道有什么不合适之处.
无论怎么简化dao层,dao对外的接口是不变的,最终业务层还是要是调用dao层的方法,还是会出现楼主的问题
我觉得既然项目业务比较简单,省掉业务层也未尝不可, 在web层直接调用dao 。
如果遇到业务操作就写在dao层,或者web层。
像cms系统就是这种情况,大多数的操作是crud,要是按照上面的写法还不疯了
可以参考一下springside2中的方法
复杂的分分层
现在的业务基本都是这种简单的情况,因为复杂的方式要求写存储过程. 简单情况如何合并?不是很理解
简单情况直接在action里调持久层代码
如
JdbcUtil.save(objet);
JdbcUtil.update(objet);
JdbcUtil.query(sql,params);
JdbcUtil可以是对hibernate,ibatis,spring jdbctemplate,dbutil,jdbc等的封装
如果比较复杂,涉及多个实体操作,事务之类的,
再整个service出来,
评论
当然这里涉及一个先有实体模型还是先有数据模型的问题!
初步想法是DAO设计如下几个通用方法:
我用的是iBatis,有一个如何确定Statementid的问题,本来考虑在如上代码中添加一个String类型的参数,或者自己设计一个StatementType实现对Statmentid 的包装,感觉是一个道理.这样坏处就是Statementid需要由Service来指定,但是这些东西职责上应该由DAO来关心!
考虑再三决定根据参数类型来决定statmentid,这样需要一定的命名规范.
如
其它类同,这样一来要求命名空间的名字同DAO的名字一致,而statement的ID同entityObject的名字一致,不过多了前缀为query,select,insert,update等.
将如上代码放入DAO的超类中,这样我每一个DAO就不需要重复写这些功能雷同的代码!不知道有什么不合适之处.
在 service层, if (pkid==null) 这样的代码从来就没写过???????
在 service层, if (pkid==null) 这样的代码从来就没写过???????
像cms系统就是这种情况,大多数的操作是crud,要是按照上面的写法还不疯了
可以参考一下springside2中的方法
复杂的分分层
现在的业务基本都是这种简单的情况,因为复杂的方式要求写存储过程. 简单情况如何合并?不是很理解
像cms系统就是这种情况,大多数的操作是crud,要是按照上面的写法还不疯了
可以参考一下springside2中的方法
视情况而定,简单的合并
复杂的分分层