今天做项目的过程中发现,多表查询的时候如果使用hibernate的DetachedCriteria离线查询方式的时候,
在多表关联的时候我们需要使用别名的方式去实现。
但是代码运行的过程中抛出了下面的异常:
之前出现异常的代码:
public String pageQuery() throws Exception { DetachedCriteria dc = pageBean.getDetachedCriteria(); //动态添加过滤条件 String addresskey = model.getAddresskey(); if(StringUtils.isNotBlank(addresskey)){ //添加过滤条件,根据地址关键字模糊查询 dc.add(Restrictions.like("addresskey", "%"+addresskey+"%")); } Region region = model.getRegion(); if(region != null){ String province = region.getProvince(); String city = region.getCity(); String district = region.getDistrict(); dc.createAlias("region", "r"); //第一次运行不会出错,但是第二次运行就会报错,因为之前已经存在了和它一样的别名,页面又是通过ajax请求的,不会刷新
if(StringUtils.isNotBlank(province)){ //添加过滤条件,根据省份模糊查询-----多表关联查询,使用别名方式实现 //参数一:分区对象中关联的区域对象属性名称 //参数二:别名,可以任意 dc.add(Restrictions.like("r.province", "%"+province+"%")); } if(StringUtils.isNotBlank(city)){ //添加过滤条件,根据市模糊查询-----多表关联查询,使用别名方式实现 //参数一:分区对象中关联的区域对象属性名称 //参数二:别名,可以任意 dc.add(Restrictions.like("r.city", "%"+city+"%")); } if(StringUtils.isNotBlank(district)){ //添加过滤条件,根据区模糊查询-----多表关联查询,使用别名方式实现 //参数一:分区对象中关联的区域对象属性名称 //参数二:别名,可以任意 dc.add(Restrictions.like("r.district", "%"+district+"%")); } } subareaService.pageQuery(pageBean); this.java2Json(pageBean, new String[]{"currentPage","detachedCriteria","pageSize", "decidedzone","subareas"}); return NONE; }
通过查阅资料发现:用DetachedCriteria或者Criteria,使用CreateAlias时,如果第2个参数alias与之前添加的重复,则会报系统异常,
然而遗憾的是我们的DetachedCriteria和Criteria并没有提供排除这种重复的方法。
要想在不修改Hibernate代码的前提下,解决这个问题,我们可以使用JAVA的反射类解决该问题。
下面提供两个函数,用于判断path和alias是否已经添加。
private boolean existAlias(Criteria c, String path, String alias) { Iterator itm = ((CriteriaImpl) c).iterateSubcriteria(); while (itm.hasNext()) { Subcriteria sub = (Subcriteria) itm.next(); if (alias.equals(sub.getAlias()) || path.equals(sub.getPath())) { return true; } } return false; } private boolean existAlias(DetachedCriteria c, String path, String alias) { Class clazz = c.getClass(); try { Field field = clazz.getDeclaredField("criteria"); field.setAccessible(true); CriteriaImpl ci; ci = (CriteriaImpl) field.get(c); return existAlias(ci, path, alias); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } return false; }
然后在之前的代码中调用提供的方法,并做如下修改可以解决问题
public String pageQuery() throws Exception { DetachedCriteria dc = pageBean.getDetachedCriteria(); //每一次分页查询的时候应该先清除之前的条件 // 动态添加过滤条件 String addresskey = model.getAddresskey(); if (StringUtils.isNotBlank(addresskey)) { // 添加过滤条件,根据地址关键字模糊查询 dc.add(Restrictions.like("addresskey", "%" + addresskey + "%")); } Region region = model.getRegion(); if (region != null) { String province = region.getProvince(); String city = region.getCity(); String district = region.getDistrict(); //创建别名之前需要判断别名是否存在。 boolean existAlias = existAlias(dc,null,"r"); if(!existAlias){//不存在就创建 dc.createAlias("region", "r"); } if (StringUtils.isNotBlank(province)) { // 添加过滤条件,根据省份模糊查询-----多表关联查询,使用别名方式实现 // 参数一:分区对象中关联的区域对象属性名称 // 参数二:别名,可以任意 dc.add(Restrictions.like("r.province", "%" + province + "%")); } if (StringUtils.isNotBlank(city)) { // 添加过滤条件,根据市模糊查询-----多表关联查询,使用别名方式实现 // 参数一:分区对象中关联的区域对象属性名称 // 参数二:别名,可以任意 dc.add(Restrictions.like("r.city", "%" + city + "%")); } if (StringUtils.isNotBlank(district)) { // 添加过滤条件,根据区模糊查询-----多表关联查询,使用别名方式实现 // 参数一:分区对象中关联的区域对象属性名称 // 参数二:别名,可以任意 dc.add(Restrictions.like("r.district", "%" + district + "%")); } } subareaService.pageQuery(pageBean); this.java2Json(pageBean, new String[] { "currentPage", "detachedCriteria", "pageSize", "decidedzone", "subareas" }); return NONE; }
别名重复问题之后,我们还需要解决的问题就是:
如何清除hibernate的上次查询条件,如果不清除,将会导致上次的查询条件和下次的查询条件合并到了一起。