手动为HibernateDAO里面的DetachedCriteria的Projection擦屁股

手动为HibernateDAO里面的DetachedCriteria的Projection擦屁股

无奈,寻找好的解决方法,把我现在擦屁股的现场记录下来。
我用GenericHibernateDAO,慢慢积累下来的,有这几个DetachedCriteria的方法:

public  List findByDetachedCriteriaByPage(
        
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实例,让用户先拿着它去查总数;然后再提供加入投影和排序信息的方法,让用户拿着它去查数据,不就解决问题了。

-------------- 示例代码:

MyDetachedCriteria dc  =   
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.Criter­ion) 
         
*/  
        
public  DetachedCriteria add( Criterion criterion ) { 
                criterions.add( criterion ); 
                
return   this
        } 


        
/**  
         * 
@see  org.hibernate.criterion.DetachedCriteria#addOrder(org.hibernate.criterion.O­rder) 
         
*/  
        
public  DetachedCriteria addOrder( Order order ) { 
                orders.add( order ); 
                
return   this
        } 


        
/**  
         * 
@see  org.hibernate.criterion.DetachedCriteria#setProjection(org.hibernate.criter­ion.Projection) 
         
*/  
        
public  DetachedCriteria setProjection( Projection projection ) { 
                
this .projection  =  projection; 
                
return   this
        } 


        
//  --------------------------------------------------------------------------- 


        
/**  
         * 根据Session实例创建一个Criteria实例,并填充入此对象自身保存的条件后返回<br> 
         * 注意:返回的Criteria实例不含排序和投影信息,用户需要自己调用fillOrdersSetting、 
         * fillProjectionSetting填充入排序和投影信息 
         * 
         * 
@see  org.hibernate.criterion.DetachedCriteria#getExecutableCriteria(org.hibernat­e.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问题的解决办法

你可能感兴趣的:(手动为HibernateDAO里面的DetachedCriteria的Projection擦屁股)