您的位置:首页 > 其它

hibernate离线查询DetachedCriteria清除上次的查询条件

2018-04-18 18:52 465 查看

1 原例概述

别名重复问题之后,我们还需要解决的问题就是:

如何清除hibernate的上次查询条件,如果不清除,将会导致上次的查询条件和下次的查询条件合并到了一起。

上次的查询条件和本次的查询条件合并到了一起。

解决之前的代码如下:

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;
}

第二次执行上面的代码将不会得到结果:原因是前一次的查询条件和本次的查询条件合并了。

这个我们可以通过发送的sql语句看出来

 

解决上述问题原理就是:本次查询之前需要清除hibernate之前的离线查询条件。

 

下面我们来看看上述问题的出现原因和解决办法。

 

2 背景

通常我们在使用离线查询技术时, 会这么使用.
如查询

BaseDict
对象对应的表中
dictTypeCode=006
的记录.

// 创建离线查询对象
DetachedCriteria dc = DetachedCriteria.forClass(BaseDict.class);
// 设置查询条件
dc.add(Restrictions.eq("dictTypeCode", "006"));
// 利用hibernateTemplate模板根据离线对象查询数据
List<BaseDict> list = (List<BaseDict>) getHibernateTemplate().findByCriteria(dc);

然而, 当我们需要再次查询

BaseDict
dictTypeCode=009
的记录时, 需要重新创建一个新的DetachedCriteria. 否则, 会将上次
dictTypeCode=006
的条件合并起来. 看下图:

 

3 源码分析

DetachedCriteria dc = DetachedCriteria.forClass(BaseDict.class);

==> 调用构造
DetachedCriteria dc = DetachedCriteria.forClass(clazz.getName);    // 通过类名构建对象

==>
CriteriaImpl(entityName, ...)    // 创建Criteria的实现类

注意: 这是实现类会在离线查询对象
dc
名为'
impl
'属性中持有.

进入

CriteriaImpl
会发现, 原来我们
add
的所有查询条件会保存在一个叫做:'CriteriaEntries'的
ArrayList
中, 并且提供了对应公有方法, 返回该list的
Iterator
迭代器.

经过上述分析, 笔者就有思路了:

  1. 利用公有方法获取
    CriteriaEntries
    的迭代器, 通过遍历删除迭代器中每一个元素, 即实现了清空条件的目的.
  2. 直接简单粗暴, 再次反射, 将
    dc
    名为'
    impl
    '属性重置, 即new一个新的
    ArrayList
    赋给它.

4 解决上述问题的代码实现

思路一: 获取迭代器, 遍历删除

    private void eraseCriteria(DetachedCriteria dc) {
try {
Field impl = dc.getClass().getDeclaredField("impl");
impl.setAccessible(true);

// 得到实现类
CriteriaImpl cimpl = (CriteriaImpl) impl.get(dc);

// 思路一: 遍历criterionEntries, 清空所有
// 利用实现类的公有方法获取迭代器
Iterator<CriteriaImpl.CriterionEntry> criterionEntryIterator = cimpl.iterateExpressionEntries();
while (criterionEntryIterator.hasNext()) {
// 删除本元素
criterionEntryIterator.remove();
}

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Log.end();

}

思路二: 直接重置
CriteriaEntries

    private void eraseCriteria(DetachedCriteria dc) {
try {
Field impl = dc.getClass().getDeclaredField("impl");
impl.setAccessible(true);

// 得到实现类
CriteriaImpl cimpl = (CriteriaImpl) impl.get(dc);

// 思路二: 再次反射, 直接将criterionEntries置空.
// 获取criterionEntries属性
Field criterionEntries = cimpl.getClass().getDeclaredField("criterionEntries");
criterionEntries.setAccessible(true);
// 重置条件list
criterionEntries.set(cimpl, new ArrayList());

} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Log.end();

}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: