无奈,寻找好的解决方法,把我现在擦屁股的现场记录下来。
我用GenericHibernateDAO,慢慢积累下来的,有这几个DetachedCriteria的方法:
final DetachedCriteria detachedCriteria, final int pagesize,
final int pageno) {
return getHibernateTemplate()
.findByCriteria(detachedCriteria, pagesize * pageno, pagesize);
}
int getCountByDetachedCriteria(
final DetachedCriteria detachedCriteria) {
Integer count = (Integer) getHibernateTemplate().execute( new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException {
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
return criteria.setProjection(Projections.rowCount())
.uniqueResult();
}
}, true );
detachedCriteria.setProjection( null );
return count.intValue();
}
生成一个DetachedCriteria,调用完getCountByDetachedCriteria以后再调用findByDetachedCriteriaByPage返回的结果居然是一个Integer……
原来getCountByDetachedCriteria方法里面对detachedCriteria.getExecutableCriteria(session)产生的criteria设定了Projection,这也影响到了DetachedCriteria本身,以后再用它作查询都回返回Projection的结果。
现在的解决方法是手动擦屁股,重新set Null。(我原先以为final关键字会保证detachedCriteria不被修改,后来发现只是引用不能修改,实例本身可以随便修改)请问大家,有什么更好的解决方案么,我决得复杂一点的话这个detachedCriteria如何能擦除回来?
Reply from BJUG Groups by whimet:
这个问题在javaeye上已经讨论过: http://www.hibernate.org.cn/viewtopic.php?t=14657
你这种情况还简单,若DetachedCriteria中包含排序设置就更不好办。
其实关键问题是DetachedCriteria.getExecutableCriteria返回的Criteria实例中已经包含了投影和排序信息,如果你想先查总数,
就得先去掉这些信息;查完总数,再查实际数据时,又得加上这些信息。而Criteria可没提供对它改来改去的接口。
不过,仔细考察一下DetachedCriteria可以发现,它类似一种暂存了查询条件的值对象,通过与一个session对象结合生成
一个“可执行的”Criteria,相当于先执行session.createCriteria,然后把自己保存的条件设置进去。
既然如此,我干脆自己提供一个类似的类,暂存查询条件,与session结合生成executableCriteria;但我先提供给用户一个只包含
查询条件的Criteria实例,让用户先拿着它去查总数;然后再提供加入投影和排序信息的方法,让用户拿着它去查数据,不就解决问题了。
-------------- 示例代码:
Criteria c = dc.getExecutableCriteria( session ); // 这里的c只包含查询条件,不包括投影和排序
c.setProjection( Projections.rowCount() );
Integer total = (Integer) c.uniqueResult(); // 得到总数
dc.fillProjectionSetting( c ); // 把dc中保存的投影信息设置进去
dc.fillOrdersSetting( c ); // 把dc中保存的排序信息设置进去
List data = c.list(); // 取回数据
-------------- MyDetachedCriteria的代码:
/**
* 单独的值对象,用于暂存与Criteria相关的查询条件,并可与Session结合生成一个可执行的Criteria<br>
* <br>
* 该类继承自DetachedCriteria只是为了保持接口兼容性,其实与DetachedCriteria没关系 <br>
* (之所以叫“Detached”,是指与Session脱离)
*/
public class MyDetachedCriteria extends DetachedCriteria {
private Class entityClass;
private ArrayList criterions = new ArrayList();
private ArrayList orders = new ArrayList();
private Projection projection;
/**
* @param entityClass
*/
public MyDetachedCriteria( Class entityClass ) {
super ( entityClass.getName() );
this .entityClass = entityClass;
}
/**
* @see org.hibernate.criterion.DetachedCriteria#add(org.hibernate.criterion.Criterion)
*/
public DetachedCriteria add( Criterion criterion ) {
criterions.add( criterion );
return this ;
}
/**
* @see org.hibernate.criterion.DetachedCriteria#addOrder(org.hibernate.criterion.Order)
*/
public DetachedCriteria addOrder( Order order ) {
orders.add( order );
return this ;
}
/**
* @see org.hibernate.criterion.DetachedCriteria#setProjection(org.hibernate.criterion.Projection)
*/
public DetachedCriteria setProjection( Projection projection ) {
this .projection = projection;
return this ;
}
// ---------------------------------------------------------------------------
/**
* 根据Session实例创建一个Criteria实例,并填充入此对象自身保存的条件后返回<br>
* 注意:返回的Criteria实例不含排序和投影信息,用户需要自己调用fillOrdersSetting、
* fillProjectionSetting填充入排序和投影信息
*
* @see org.hibernate.criterion.DetachedCriteria#getExecutableCriteria(org.hibernate.Session)
*/
public Criteria getExecutableCriteria( Session session ) {
Criteria result = session.createCriteria( entityClass );
for ( Iterator i = criterions.iterator(); i.hasNext(); ) {
Criterion criterion = ( Criterion ) i.next();
result.add( criterion );
}
return result;
}
/**
* 填充入排序信息设置
*
* @param criteria
* @return
*/
public Criteria fillOrdersSetting( Criteria criteria ) {
for ( Iterator i = orders.iterator(); i.hasNext(); ) {
Order order = ( Order ) i.next();
criteria.addOrder( order );
}
return criteria;
}
/**
* 填充入投影信息设置
*
* @param criteria
* @return
*/
public Criteria fillProjectionSetting( Criteria criteria ) {
criteria.setProjection( projection );
return criteria;
}
}
Update 2006-6-6 20:52:
恩,谢谢诸位。想了想还是DetachedCriteria设计的目的和我想要的不一样。DetachedCriteria目前只是为了脱离session就可以构造,而并不是为了反复使用。
因为:1、它里面只包装了一个CriteriaImpl,所以实际上两者生命周期比较一致。而且每次getExecutableCriteria都直接返回这个CriteriaImpl,而不是重新创建,这就造成了容易被意外修改。 2、它没有提供与add对应的remove方法,这就造成它只能累积而不能擦拭(对于Order),用反射就太脏了。
whimet写的这个MyDetachedCriteria倒是可以解决问题,更符合我们想要复用DetachedCriteria的需求,不过我觉得调用起来接口上不太统一。Order和Projection一般不用复用,我想就可以不用实现DetachedCriteria接口,干脆修改GenericDAO的方法好了,写个接受List<Criteria>或者Criteria...的方法对应就可以了。反正觉得DetachedCriteria目前的实现是不如意,hack它吧……我就想提供remove这些Order和Projection的方法。
这里用反射解决了他遇到的Order的问题,大家可以参考,看来DetachedCriteria目前实现的还不够令人满意:
关于Hibernate的DetachedCriteria查询的addOrder问题的解决办法