Tin's Blog

You are coming a long way, baby~Thinking, feeling, memory...

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  128 随笔 :: 0 文章 :: 221 评论 :: 0 Trackbacks

无奈,寻找好的解决方法,把我现在擦屁股的现场记录下来。
我用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问题的解决办法

posted on 2006-06-06 11:28 Tin 阅读(4599) 评论(3)  编辑  收藏 所属分类: Hibernate相关

评论

# re: 手动为HibernateDAO里面的DetachedCriteria的Projection擦屁股 2006-06-06 11:43 Guest
Object 的Final 只是object reference address final 吧,不是object final的吧  回复  更多评论
  

# re: 手动为HibernateDAO里面的DetachedCriteria的Projection擦屁股 2006-06-06 12:39 Tin
对呀,只是引用地址不可修改,里面的东西还可以改。所以想找个好点的方案,比如克隆一个本地版本出来?  回复  更多评论
  

# re: 手动为HibernateDAO里面的DetachedCriteria的Projection擦屁股 2006-09-28 09:41 coolfish
springside 的实现方式还不错吧:
CriteriaImpl impl = (CriteriaImpl) criteria;

//先把Projection和OrderBy条件取出来,清空两者来执行Count操作
Projection projection = impl.getProjection();
List<OrderEntry> orderEntries;
try {
orderEntries = (List) BeanUtils.getPrivateProperty(impl, "orderEntries");
BeanUtils.setPrivateProperty(impl, "orderEntries", new ArrayList());
}
catch (Exception e) {
throw new InternalError(" Runtime Exception impossibility throw ");
}

//执行查询
int totalCount = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();

//将之前的Projection和OrderBy条件重新设回去
criteria.setProjection(projection);
if (projection == null) {
criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
}

try {
BeanUtils.setPrivateProperty(impl, "orderEntries", orderEntries);
} catch (Exception e) {
throw new InternalError(" Runtime Exception impossibility throw ");
}

return getPageResult(criteria, totalCount, pageNo, pageSize);

这些人看了CriteriaImpl里的filed,拿beanUtil给替换了  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: