最近做的网站主要就是CRUD操作,如果对每个实体都编写对应的DAO类,并且每个DAO类都写上CRUD方法,显然会出现大量的复制、粘贴操作,这样写的代码冗余太多,因此必须要提供上层抽象的DAO类,并且实现通用的CRUD方法。
以下是一些网友的见解:
http://www.ibm.com/developerworks/cn/java/j-genericdao.html
在实际应用中,开发者一般会在Spring支持类的基础上编写自己的DAO其类,进行自己的封装,以获得泛型的支持并提供自己的代理方法使子类避免显式调 用模板类的代码。DAO基类并不需要对模板类的所有方法进行代理,仅对常用的方法(如CRUD操作方法)进行代理,而对不常用的方法则可要求子类显式调用 模板实例完成。这样,一方面简化常用方法的调用,另一方面又使基类不过于复杂。 由于一般的实体类对应的DAO都必须拥有CRUD操作,与其在每个实体DAO接口中复杂定义这些方法,不如提供一个通用的DAO接口。具体的实体DAO接 口可以扩展这个通用DAO接口并定义实体类相关的其它操作方法就可以了。以Hibernate为例,图 2中给出了典型的DAO类的结构:
BaseDao<T>通用基础接口对所有实体DAO接口的通用方法进行了抽象并提供泛型的支持,其代码如代码清单 12所示:
public interface BaseDao<T> {
T get(Serializable id); ①通过泛型的支持,加载实体的方法仅需要一个主键就可以了
List<T> getAll(); ②直接返回强类型的集合类
③泛型并没有给保存、删除和更改的操作带来方便,因此这些方法可以不使用泛型
//但我感觉这里泛型应该更好啊,即把下面Object都改成T
//这些方法的实现基本上都是"getHibernateTemplate().save(o);"的形式
void save(Object o);
void remove(Object o);
void update(Object o);
}
在没有泛型支持的情况下查询实体,不但要指定主键值还需要指定实体类型,不但方法入参比较复杂,调用者还需要对返回的结果对象进行强制类型转换,这两种缺点都可以通过在基类中引入泛型得到彻底的克服。
查询接口方法的设计
DAO层类中除了CRUD的数据操作外,另一个重要的操作就是根据条件查询结果。不同的ORM框架都允许你动态绑定参数指定查询条件。查询条件项数目往往 是不固定的,如既可能要求以userName为条件查询User,也可能要求以userName+status等组合条件查询User。条件项数目的不定 性给查询接口方法的设计造成为一定的困难, 实体DAO定义带参的查询方法时,一般有5种方式,下面分别对这些方式进行介绍。
方式1:每一个条件项参数对应一个入参
在查询方法中为每一个用到查询条件项定义一个对应的入参,如:
List findOrder(String hql,Date startTime,Date endTime,int deptId)
这种方式的方法签名含义清晰,可读性强,内部的逻辑处理简单,但接口稳定性差。假如这个findOrder()方法需要添加一个userName的条件, 有两种重构的方式:第一,调整方法的签名,新增一个String userName入参,这种重构被认为是不健康的重构,因为它违反软件设计中经典的“开-闭原则”,此外如果条件项数目很多,方法签名将显得过于拖沓;第 二,在DAO类中新增一个重载查询方法,如果查询条件项的组合数过多,DAO类的方法数目将直线上升,整个实体DAO类将显得臃肿笨重。
方式2:使用数组传递条件项参数
通过数组的方式传递查询条件项参数,由于参数类型的不一致性,要求参数类型采用Object[]:
List findOrder(String hql,Object[] params) 这种方法接口可以应付查询条件项参数组合的多样性并保持接口的稳定性,开发者必须在方法内部从数组中获取参数再传递给查询引擎。缺点是方法的可读性不强, 调用者往往需要通过查看接口对应的Javadoc才能正确使用。此外,在JDK 5.0以下的版本中,因为没有自动拆/解包的语言特性,调用前必须对基本类型的参数使用包裹类封装,有时在方法内部还需要进行相反的过程,在使用上较为不 便。不过由于Spring为支持的ORM框架都提供了类似的查询接口(如HibernateTemplate#find(String queryString, Object[] values)),所以方法内部的处理相对还是比较简单的。开发者采用如下的方式进行调用:
Object[] params = new Object[]...{"112",new Integer(12),new Integer(1)};
list = findOrder(hql,params);
方式3:使用JDK 5.0的不定参数
如果在JDK 5.0中,则可以采用不定参数进行方式签名,这种方式在逻辑上和方式2并无多大的区别:
List findOrder(String hql,Object... params)
方式4:将查询条件项参数封装成对象
为了提高方法1中方法签名简洁性,增强方法2、3中方法签名的可读性,方式4提出将查询条件项参数封装成一个对象的思路,查询方法使用查询条件对象传递查询条件:
List<Order> findOrder(String hql,OrderQueryParam param)
OrderQueryParam查询条件对象封装了hql查询语句可能会用到的条件项参数,在查询方法内部,开发者必须判断查询条件对象的属性并正确绑定 条件项参数。由于需要为条件项参数定义一个类,因此会造成类数目的膨胀,有时甚至一个实体DAO需要对应多个查询条件封装类。另外,当需要添加一个新的条 件项参数时,条件封装类还需要进行相应调整。
OrderQueryParam param = new OrderQueryParam();
param.setUserName("zhang");
param.setDeptId(12);
param.setStatus()0;
list = findOrder(hql,param);
方式5:使用Map传递条件项参数
另一种被广泛使用的方法是采用Map传递条件项参数,键为参数名,值为参数值:
List<Order> findOrder(String hql,Map params)
这种方式可以在条件项变化,接口方法依然可以保持稳定,同时通过键指定参数名的方式在一定程度上解决了接口的健壮性(当调用者指定错误参数名时容易得到错误报警)。但和方法2、3一样调用者必须通过接口Javadoc才能进行调用。
Map paramMap = new HashMap();
paramMap.put("userName","zhang");
paramMap.put("deptId",12);
paramMap.put("status",0);
list = findOrder(hql,paramMap);
小结
虽然Spring为我们提供了使用各种ORM框架的方便类,但这离实际的应用开发还有一段需要缩短的距离。具体的应用一般会在Spring所提供的支持类 基础上提供过一步的封装,建立方便的DAO基类,简化接口方法添加泛型支持等功能。一个设计良好的DAO基类可以大大减少DAO层整体代码的总量,提高项 目的开发效率。
本文探讨了在软件开发中如何设计一个通用的DAO层,包括实现泛型支持以简化CRUD操作,并讨论了五种不同的查询接口设计方法及其优缺点。


3080

被折叠的 条评论
为什么被折叠?



