论坛首页 Java企业应用论坛

应用Hibernate3的DetachedCriteria实现分页查询

浏览 154804 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-08-25  
破碎虚空 和 yangbo9229 遇到的的确是个问题,不知道为什么大家都没有注意到他们的话呢?

我来说一下我调试下来的观点:

在robin的HibernateCallback()内,不论先做count计算,还是先做list查询,都有问题。

robbin原来的代码:先count,然后用的setProjection(null),再查询,不过经测试,查出来的都是Object[],而不是原先DetachedCriteria.forClass(xxx)的内容了。

samuel_bai提到把两者的顺序换一下即可,但是换了以后照样有问题:
就是setFirstResult和setMaxResults的值还保留在criteria里面,查询count的时候,导致count变小,甚至出错(在查不到数据的时候,调用Integer.intValue()报NullPointException)。

我最后想了两个办法解决,
1:
采用robbin的顺序,不过取size的时候用的是:
int totalCount = getHibernateTemplate().findByCriteria(detachedCriteria).size();
so ez?但是这样比较消耗内存,但是不用调用HibernateCallback的那段了,不用设置projection,后面的查询问题迎刃而解,整个代码看起来很清爽:
		int totalCount = getHibernateTemplate();
				.findByCriteria(detachedCriteria);.size();;
		List<T> list = getHibernateTemplate();.findByCriteria(detachedCriteria,
				startIndex, pageSize);;
		return new PageInfo<T>(list, totalCount, pageSize, startIndex);;


2:
采用samuel_bai的顺序,先查询list,后设置count,这样setProjection的代码就跑到最后去了,可以不用设置了,自然也没有问题。但是pageSize, startIndex的问题怎么办呢?
没办法,我选择了一种很土的办法:
					criteria.setFirstResult(0);;
					criteria.setMaxResults(999);;

呵呵,大家别笑,至少是可行的了。


以上,就是我认真看完整个帖子后,郁闷了n小时后,蹩出来的一点点东西,大家笑纳。
0 请登录后投票
   发表时间:2006-09-08  
昨天看到这篇帖子,学习了一整天,感觉受益非浅。
不过始终有一点疑问,这样做的话WEB层就会出现Hibernate的API了,这是不是有点违背分层的思想啊?
0 请登录后投票
   发表时间:2006-09-12  
    public TokenStream tokenStream(String fieldName, Reader reader)
    {
        LetterTokenizer tokenizer = new LetterTokenizer(reader);
        TokenStream result = null;
        result = new LowerCaseFilter(tokenizer);
        result = new StopFilter(result, StopAnalyzer.ENGLISH_STOP_WORDS);
        result = new PorterStemFilter(result);
        return result;
    }
}
0 请登录后投票
   发表时间:2006-09-13  
建议让hibernate重写.size 方法
太浪费代码了。。。。。
或者有我没发现么?
0 请登录后投票
   发表时间:2006-09-15  
条理很清晰滴说
0 请登录后投票
   发表时间:2006-09-15  
downpour 写道
连续看了两篇robbin有关DetachedCriteria的介绍,感觉真的不错,尤其是上面的示例代码,让我着实觉得该对我原来的分页查询做一下代码重构了。
public class UserDAOImpl extends BaseDAOHibernateImpl implements UserDAO {

    public List getUserByName(String name) throws HibernateException {
        String querySentence = "FROM user in class com.adt.po.User WHERE user.name=:name";
        Query query = getSession().createQuery(querySentence);
        query.setParameter("name", name);
        return query.list(); 
    }
    public int getUserCount() throws HibernateException {
        int count = 0;
        String querySentence = "SELECT count(*) FROM user in class com.adt.po.User";
        Query query = getSession().createQuery(querySentence);
        count = ((Integer)query.iterate().next()).intValue();
        return count;
    }

    public List getUserByPage(Page page) throws HibernateException {
        String querySentence = "FROM user in class com.adt.po.User";
        Query query = getSession().createQuery(querySentence);
        query.setFirstResult(page.getBeginIndex())
        	 .setMaxResults(page.getEveryPage());
        return query.list();
    }

}



每次都getSession(),怎么没有见关闭Session的语句?
0 请登录后投票
   发表时间:2006-09-25  
楼上的,session关闭是写在另外一个地方的.你没有看到getSession()方法也没有写如何获取session的吗?
0 请登录后投票
   发表时间:2006-12-12  
downpour 写道
连续看了两篇robbin有关DetachedCriteria的介绍,感觉真的不错,尤其是上面的示例代码,让我着实觉得该对我原来的分页查询做一下代码重构了。

我把原本我的做法也提供出来供大家讨论吧:

首先,为了实现分页查询,我封装了一个Page类:
/*Created on 2005-4-14*/
package org.flyware.util.page;

/**
 * @author Joa
 *
 */
public class Page {
    
    /** imply if the page has previous page */
    private boolean hasPrePage;
    
    /** imply if the page has next page */
    private boolean hasNextPage;
        
    /** the number of every page */
    private int everyPage;
    
    /** the total page number */
    private int totalPage;
       
    /** the number of current page */
    private int currentPage;
    
    /** the begin index of the records by the current query */
    private int beginIndex;
    
    
    /** The default constructor */
    public Page(){
        
    }
    
    /** construct the page by everyPage 
     * @param everyPage
     * */
    public Page(int everyPage){
        this.everyPage = everyPage;
    }
    
    /** The whole constructor */
    public Page(boolean hasPrePage, boolean hasNextPage,  
            	 int everyPage, int totalPage, 
            	 int currentPage, int beginIndex) {
        this.hasPrePage = hasPrePage;
        this.hasNextPage = hasNextPage;
        this.everyPage = everyPage;
        this.totalPage = totalPage;
        this.currentPage = currentPage;
        this.beginIndex = beginIndex;
    }

    /**
     * @return 
     * Returns the beginIndex.
     */
    public int getBeginIndex() {
        return beginIndex;
    }
   


这样就可以通过配置文件和OGNL的共同作用来对page对象设置初值了。并可以通过随意修改配置文件来修改每页需要显示的记录数。

注:上面的<param>的配置,还需要webwork和Spring整合的配合。


有查询条件怎么办?
0 请登录后投票
   发表时间:2006-12-15  
sakis 写道
samuel_bai 写道
引用
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                                int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
                                criteria.setProjection(null);
                                List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
这段代码有如下几个问题:
1。如果cirteria设置了projection,这里将其改变,那么查出的items则是不正确的。因此只要将tocalcount和items的位置换一下即可了
2。如果criteria设置了order的话,那么criteria生成的sql将会有语法错误,比如order by中的某某字段不在聚合函数中,如果能将criteria的order去掉就好了,可是criteria没有这个api......


- 1.cirteria设置了projection统计完rowCount后又setProjection(null),所以“基本”上应该是可行的。
- 2.这个order问题的确很伤心,我的做法是将Order[]作为一个参数传入,在rowCount统计完成后,再在criteria中加入。

这种分页做法很优美,完全把分页程序与具体表分离了,复用性很高。遗憾的是DetachedCriteria/Criteria现在实现的还不是十分完善,比如:
我用的hibernate是3.0.5,发现多次使用同一个DetachedCriteria对象后,在作rowCount projection时会出错,返回null(可前几次都返回了正确的结果)。

该方法对于单表查询应该还是没多大问题的,对于某些复杂的查询条件需要做更多的测试。


cirteria设置了projection统计完rowCount后又setProjection(null),所以“基本”上应该是可行的。

事实是不可行的,不可行的原因在于多表查询确实会出现返回的List中是对象数组这种情况,所以确实应该调换Items和计算总数的位置,先查items,再进行总数查询,这样肯定是没有问题的
0 请登录后投票
   发表时间:2006-12-25  
ahuaxuan 写道
sakis 写道
samuel_bai 写道
引用
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                                int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
                                criteria.setProjection(null);
                                List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
这段代码有如下几个问题:
1。如果cirteria设置了projection,这里将其改变,那么查出的items则是不正确的。因此只要将tocalcount和items的位置换一下即可了
2。如果criteria设置了order的话,那么criteria生成的sql将会有语法错误,比如order by中的某某字段不在聚合函数中,如果能将criteria的order去掉就好了,可是criteria没有这个api......


- 1.cirteria设置了projection统计完rowCount后又setProjection(null),所以“基本”上应该是可行的。
- 2.这个order问题的确很伤心,我的做法是将Order[]作为一个参数传入,在rowCount统计完成后,再在criteria中加入。

这种分页做法很优美,完全把分页程序与具体表分离了,复用性很高。遗憾的是DetachedCriteria/Criteria现在实现的还不是十分完善,比如:
我用的hibernate是3.0.5,发现多次使用同一个DetachedCriteria对象后,在作rowCount projection时会出错,返回null(可前几次都返回了正确的结果)。

该方法对于单表查询应该还是没多大问题的,对于某些复杂的查询条件需要做更多的测试。


cirteria设置了projection统计完rowCount后又setProjection(null),所以“基本”上应该是可行的。

事实是不可行的,不可行的原因在于多表查询确实会出现返回的List中是对象数组这种情况,所以确实应该调换Items和计算总数的位置,先查items,再进行总数查询,这样肯定是没有问题的

  楼上说的调换items和计算总数的位置是这样的吗?
     
  List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();  

        criteria.setProjection(null);   
       int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();                 PaginationSupport ps = new PaginationSupport(items, totalCount, pageSize, startIndex); 


  如果是这样的话,我试了一下,出现空指针异常;如果不是这样的请给说明一下;
我用robbin的方法,对一个没有关联的表,好象没什么问题,但是,我做了一个one to one的映射后,如下使用:

DetachedCriteria detachedCriteria=DetachedCriteria.forClass(SysUser.class);
		int pageSize=2;
		int startIndex=1;
		 
		PaginationSupport ps=sysUserService.findPageByCriteria(detachedCriteria, pageSize, startIndex);
		if(ps!=null){
			System.out.println(ps.getPageSize());
			System.out.println(ps.getNextIndex());
			System.out.println(ps.getPreviousIndex());
			System.out.println(ps.getIndexes().length);
			System.out.println(ps.getTotalCount());
			List list=ps.getItems();
			
			for(int i=0;i<list.size();i++){
				SysUser sysUser=(SysUser)list.get(i);
				System.out.println(sysUser.getUserid());
			}
		}

映射文件如下:
  <class name="com.hengji.model.SysUser" table="SYS_USER" schema="SUGARCRM">
        <id name="userid" type="java.lang.String">
            <column name="USERID" length="50" />
            <generator class="assigned" />
        </id>
        <property name="password" type="java.lang.String">
            <column name="PASSWORD" length="50" not-null="true" />
        </property>
        <property name="createDate" type="java.util.Date">
            <column name="CREATE_DATE" length="7" />
        </property>
        <property name="updateDate" type="java.util.Date">
            <column name="UPDATE_DATE" length="7" />
        </property>
        <property name="roleId" type="java.lang.String">
            <column name="ROLE_ID" length="20" />
        </property>
        <property name="useFlag" type="java.lang.String">
            <column name="USE_FLAG" length="1" not-null="true" />
        </property>
        <one-to-one name="userInfo"  cascade="all" class="com.hengji.model.UserInfo"/>      
    </class>

  这样查出的总数,没有什么问题,可是items是一个对象数组,应该是被分成了sysuser和userinfo两个对象了,我把他转化为sysuser时,报类型转换异常;请问大家这个问题该如何解决?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics