Hibernate支持DetachedCriteria这是一个非常有意义的特性!我们知道在常规的Web编程中有大量的动态条件查询即用户在网页上面自由选择某些条件程序根据用户的选择条件动态生成SQL语句进行查询
针对这种需求对于分层应用程序来说Web层需要传递一个查询的条件列表给业务层对象业务层对象获得这个条件列表之后然后依次取出条件构造查询语句这里的一个难点是条件列表用什么来构造?传统上使用Map但是这种方式缺陷很大Map可以传递的信息非常有限只能传递name和value无法传递究竟要做怎样的条件运算究竟是大于小于like还是其它的什么业务层对象必须确切掌握每条entry的隐含条件因此一旦隐含条件改变业务层对象的查询构造算法必须相应修改但是这种查询条件的改变是隐式约定的而不是程序代码约束的因此非常容易出错
DetachedCriteria可以解决这个问题即在web层程序员使用DetachedCriteria来构造查询条件然后将这个DetachedCriteria作为方法调用参数传递给业务层对象而业务层对象获得DetachedCriteria之后可以在session范围内直接构造Criteria进行查询就此查询语句的构造完全被搬离到web层实现而业务层则只负责完成持久化和查询的封装即可与查询条件构造完全解耦非常完美!这恐怕也是以前很多企图在web层代码中构造HQL语句的人想实现的梦想吧!
示例代码片段如下
web层程序构造查询条件
java代码
DetachedCriteria detachedCriteria = DetachedCriteriaforClass(Departmentclass);
detachedCriteriaadd(Restrictionseq(name department))createAlias(employees e)add(Restrictionsgt((eage) new Integer()));
Department和Employee是一对多关联查询条件为
名称是department开发部门部门里面的雇员年龄大于岁
业务层对象使用该条件执行查询
java代码
detachedCriteriagetExecutableCriteria(session)list();
最大的意义在于业务层代码是固定不变的所有查询条件的构造都在web层完成业务层只负责在session内执行之这样代码就可放之四海而皆准都无须修改了
然而Spring和Hibernate的DetachedCriteria有不兼容的问题因此在Spring环境下面使用Hibernate需要注意
Spring的HibernateTemplate提供了Hibernate的完美封装即通过匿名类实现回调来保证Session的自动资源管理和事务的管理其中核心方法是
java代码
HibernateTemplateexecute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
}
}
回调方法提供了session作为参数有了session就可以自由的使用Hibernate API编程了使用了spring的之后代码修改如下
web层代码
java代码
DetachedCriteria detachedCriteria = DetachedCriteriaforClass(Departmentclass);
detachedCriteriacreateAlias(employees e)add(Restrictionseq(name department))add(Restrictionsgt((eage) new Integer()));
departmentManagerfindByCriteria(detachedCriteria);
构造detachedCriteria作为参数传递给departmentManager
业务层代码使用springDepartmentManager的findByCriteria如下
java代码
public List findByCriteria(final DetachedCriteria detachedCriteria) {
return (List) getHibernateTemplate()execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Criteria criteria = detachedCriteriagetExecutableCriteria(session);
return criterialist();
}
});
}
实际上也就是
java代码
Criteria criteria = detachedCriteriagetExecutableCriteria(session);
return criterialist();
而已
但是该程序代码执行会抛出强制类型转换异常!
我跟蹤了一下spring和Hibernate源代码原因如下
spring的HibernateTemplate的execute方法提供的回调接口具有Session作为参数但是实际上默认情况下HibernateTemplate传递给回调接口的session并不是orghibernateimplSessionImpl类而是SessionImpl类的一个Proxy类之所以替换成为一个Proxy类HibernateTemplate的注释说明Proxy提供了一些额外的功能包括自动设置CachableTransaction的超时时间Session资源的更积极的关闭等等
java代码
private boolean exposeNativeSession = false;
execute方法内部
Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));
但是遗憾的是Hibernate的DetachedCriteria的setExecutableCriteria方法却要求将session参数强制转为SessionImpl但是spring传过来的却是一个Proxy类因此就报错了
java代码
public Criteria getExecutableCriteria(Session session) {
implsetSession( (SessionImpl) session ); // 要求SessionImplSpring传递的是Proxy
return impl;
}
解决方法禁止Spring的HibernateTemplate传递Proxy类强制要求它传递真实的SessionImpl类即给exexute方法增加一个参数提供参数为true如下
java代码
public List findByCriteria(final DetachedCriteria detachedCriteria) {
return (List) getHibernateTemplate()execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Criteria criteria = detachedCriteriagetExecutableCriteria(session);
return criterialist();
}
} true);
}
附一个进行模糊查询的例子
public PaginationSupport findPageByCriteria(int startIndex int pageSize String sortColumnId Boolean bSortOrder final String likeValue) {
DetachedCriteria detachedCriteria = DetachedCriteria forClass(Timeclass);
// like condition
if ((likeValue != null && likeValuetrim()length() > )) {
detachedCriteriaadd(Restrictionsor(RestrictionssqlRestriction(statime like ? % + likeValue + % HibernateSTRING)RestrictionssqlRestriction(endtime like ? % + likeValue + % HibernateSTRING)));
}