gebacao的自留地

常用链接

统计

最新评论

应用Hibernate3的DetachedCriteria实现分页查询

来至http://forum.javaeye.com/viewtopic.php?t=14657&postdays=0&postorder=asc&start=0


Hibernate3提供了DetachedCriteria,使得我们可以在Web层构造detachedCriteria,然后调用业务层Bean,进行动态条件查询,根据这一功能,我设计了通用的抽象Bean基类和分页类支持,代码来自于Quake Wang的javaeye-core包的相应类,然后又做了很多修改。

分页支持类:

java代码: 


package com.javaeye.common.util;

import java.util.List;

publicclass PaginationSupport {

        publicfinalstaticint PAGESIZE = 30;

        privateint pageSize = PAGESIZE;

        privateList items;

        privateint totalCount;

        privateint[] indexes = newint[0];

        privateint startIndex = 0;

        public PaginationSupport(List items, int totalCount){
                setPageSize(PAGESIZE);
                setTotalCount(totalCount);
                setItems(items);               
                setStartIndex(0);
        }

        public PaginationSupport(List items, int totalCount, int startIndex){
                setPageSize(PAGESIZE);
                setTotalCount(totalCount);
                setItems(items);               
                setStartIndex(startIndex);
        }

        public PaginationSupport(List items, int totalCount, int pageSize, int startIndex){
                setPageSize(pageSize);
                setTotalCount(totalCount);
                setItems(items);
                setStartIndex(startIndex);
        }

        publicList getItems(){
                return items;
        }

        publicvoid setItems(List items){
                this.items = items;
        }

        publicint getPageSize(){
                return pageSize;
        }

        publicvoid setPageSize(int pageSize){
                this.pageSize = pageSize;
        }

        publicint getTotalCount(){
                return totalCount;
        }

        publicvoid setTotalCount(int totalCount){
                if(totalCount > 0){
                        this.totalCount = totalCount;
                        int count = totalCount / pageSize;
                        if(totalCount % pageSize > 0)
                                count++;
                        indexes = newint[count];
                        for(int i = 0; i < count; i++){
                                indexes[i] = pageSize * i;
                        }
                }else{
                        this.totalCount = 0;
                }
        }

        publicint[] getIndexes(){
                return indexes;
        }

        publicvoid setIndexes(int[] indexes){
                this.indexes = indexes;
        }

        publicint getStartIndex(){
                return startIndex;
        }

        publicvoid setStartIndex(int startIndex){
                if(totalCount <= 0)
                        this.startIndex = 0;
                elseif(startIndex >= totalCount)
                        this.startIndex = indexes[indexes.length - 1];
                elseif(startIndex < 0)
                        this.startIndex = 0;
                else{
                        this.startIndex = indexes[startIndex / pageSize];
                }
        }

        publicint getNextIndex(){
                int nextIndex = getStartIndex() + pageSize;
                if(nextIndex >= totalCount)
                        return getStartIndex();
                else
                        return nextIndex;
        }

        publicint getPreviousIndex(){
                int previousIndex = getStartIndex() - pageSize;
                if(previousIndex < 0)
                        return0;
                else
                        return previousIndex;
        }

}



抽象业务类
java代码: 


/**
* Created on 2005-7-12
*/

package com.javaeye.common.business;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.javaeye.common.util.PaginationSupport;

publicabstractclass AbstractManager extends HibernateDaoSupport {

        privateboolean cacheQueries = false;

        privateString queryCacheRegion;

        publicvoid setCacheQueries(boolean cacheQueries){
                this.cacheQueries = cacheQueries;
        }

        publicvoid setQueryCacheRegion(String queryCacheRegion){
                this.queryCacheRegion = queryCacheRegion;
        }

        publicvoid save(finalObject entity){
                getHibernateTemplate().save(entity);
        }

        publicvoid persist(finalObject entity){
                getHibernateTemplate().save(entity);
        }

        publicvoid update(finalObject entity){
                getHibernateTemplate().update(entity);
        }

        publicvoid delete(finalObject entity){
                getHibernateTemplate().delete(entity);
        }

        publicObject load(finalClass entity, finalSerializable id){
                return getHibernateTemplate().load(entity, id);
        }

        publicObject get(finalClass entity, finalSerializable id){
                return getHibernateTemplate().get(entity, id);
        }

        publicList findAll(finalClass entity){
                return getHibernateTemplate().find("from " + entity.getName());
        }

        publicList findByNamedQuery(finalString namedQuery){
                return getHibernateTemplate().findByNamedQuery(namedQuery);
        }

        publicList findByNamedQuery(finalString query, finalObject parameter){
                return getHibernateTemplate().findByNamedQuery(query, parameter);
        }

        publicList findByNamedQuery(finalString query, finalObject[] parameters){
                return getHibernateTemplate().findByNamedQuery(query, parameters);
        }

        publicList find(finalString query){
                return getHibernateTemplate().find(query);
        }

        publicList find(finalString query, finalObject parameter){
                return getHibernateTemplate().find(query, parameter);
        }

        public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria){
                return findPageByCriteria(detachedCriteria, PaginationSupport.PAGESIZE, 0);
        }

        public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, finalint startIndex){
                return findPageByCriteria(detachedCriteria, PaginationSupport.PAGESIZE, startIndex);
        }

        public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, finalint pageSize,
                        finalint startIndex){
                return(PaginationSupport) getHibernateTemplate().execute(new HibernateCallback(){
                        publicObject doInHibernate(Session session)throws HibernateException {
                                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();
                                PaginationSupport ps = new PaginationSupport(items, totalCount, pageSize, startIndex);
                                return ps;
                        }
                }, true);
        }

        publicList findAllByCriteria(final DetachedCriteria detachedCriteria){
                return(List) getHibernateTemplate().execute(new HibernateCallback(){
                        publicObject doInHibernate(Session session)throws HibernateException {
                                Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                                return criteria.list();
                        }
                }, true);
        }

        publicint getCountByCriteria(final DetachedCriteria detachedCriteria){
                Integer count = (Integer) getHibernateTemplate().execute(new HibernateCallback(){
                        publicObject doInHibernate(Session session)throws HibernateException {
                                Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                                return criteria.setProjection(Projections.rowCount()).uniqueResult();
                        }
                }, true);
                return count.intValue();
        }
}




用户在web层构造查询条件detachedCriteria,和可选的startIndex,调用业务bean的相应findByCriteria方法,返回一个PaginationSupport的实例ps。

ps.getItems()得到已分页好的结果集
ps.getIndexes()得到分页索引的数组
ps.getTotalCount()得到总结果数
ps.getStartIndex()当前分页索引
ps.getNextIndex()下一页索引
ps.getPreviousIndex()上一页索引


连续看了两篇robbin有关DetachedCriteria的介绍,感觉真的不错,尤其是上面的示例代码,让我着实觉得该对我原来的分页查询做一下代码重构了。

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

首先,为了实现分页查询,我封装了一个Page类:
java代码: 


/*Created on 2005-4-14*/
package org.flyware.util.page;

/**
* @author Joa
*
*/

publicclass Page {
   
    /** imply if the page has previous page */
    privateboolean hasPrePage;
   
    /** imply if the page has next page */
    privateboolean hasNextPage;
       
    /** the number of every page */
    privateint everyPage;
   
    /** the total page number */
    privateint totalPage;
       
    /** the number of current page */
    privateint currentPage;
   
    /** the begin index of the records by the current query */
    privateint 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.
     */

    publicint getBeginIndex(){
        return beginIndex;
    }
   
    /**
     * @param beginIndex
     * The beginIndex to set.
     */

    publicvoid setBeginIndex(int beginIndex){
        this.beginIndex = beginIndex;
    }
   
    /**
     * @return
     * Returns the currentPage.
     */

    publicint getCurrentPage(){
        return currentPage;
    }
   
    /**
     * @param currentPage
     * The currentPage to set.
     */

    publicvoid setCurrentPage(int currentPage){
        this.currentPage = currentPage;
    }
   
    /**
     * @return
     * Returns the everyPage.
     */

    publicint getEveryPage(){
        return everyPage;
    }
   
    /**
     * @param everyPage
     * The everyPage to set.
     */

    publicvoid setEveryPage(int everyPage){
        this.everyPage = everyPage;
    }
   
    /**
     * @return
     * Returns the hasNextPage.
     */

    publicboolean getHasNextPage(){
        return hasNextPage;
    }
   
    /**
     * @param hasNextPage
     * The hasNextPage to set.
     */

    publicvoid setHasNextPage(boolean hasNextPage){
        this.hasNextPage = hasNextPage;
    }
   
    /**
     * @return
     * Returns the hasPrePage.
     */

    publicboolean getHasPrePage(){
        return hasPrePage;
    }
   
    /**
     * @param hasPrePage
     * The hasPrePage to set.
     */

    publicvoid setHasPrePage(boolean hasPrePage){
        this.hasPrePage = hasPrePage;
    }
   
    /**
     * @return Returns the totalPage.
     *
     */

    publicint getTotalPage(){
        return totalPage;
    }
   
    /**
     * @param totalPage
     * The totalPage to set.
     */

    publicvoid setTotalPage(int totalPage){
        this.totalPage = totalPage;
    }
   
}



上面的这个Page类对象只是一个完整的Page描述,接下来我写了一个PageUtil,负责对Page对象进行构造:
java代码: 


/*Created on 2005-4-14*/
package org.flyware.util.page;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* @author Joa
*
*/

publicclass PageUtil {
   
    privatestaticfinal Log logger = LogFactory.getLog(PageUtil.class);
   
    /**
     * Use the origin page to create a new page
     * @param page
     * @param totalRecords
     * @return
     */

    publicstatic Page createPage(Page page, int totalRecords){
        return createPage(page.getEveryPage(), page.getCurrentPage(), totalRecords);
    }
   
    /** 
     * the basic page utils not including exception handler
     * @param everyPage
     * @param currentPage
     * @param totalRecords
     * @return page
     */

    publicstatic Page createPage(int everyPage, int currentPage, int totalRecords){
        everyPage = getEveryPage(everyPage);
        currentPage = getCurrentPage(currentPage);
        int beginIndex = getBeginIndex(everyPage, currentPage);
        int totalPage = getTotalPage(everyPage, totalRecords);
        boolean hasNextPage = hasNextPage(currentPage, totalPage);
        boolean hasPrePage = hasPrePage(currentPage);
       
        returnnew Page(hasPrePage, hasNextPage, 
                                everyPage, totalPage,
                                currentPage, beginIndex);
    }
   
    privatestaticint getEveryPage(int everyPage){
        return everyPage == 0 ? 10 : everyPage;
    }
   
    privatestaticint getCurrentPage(int currentPage){
        return currentPage == 0 ? 1 : currentPage;
    }
   
    privatestaticint getBeginIndex(int everyPage, int currentPage){
        return(currentPage - 1) * everyPage;
    }
       
    privatestaticint getTotalPage(int everyPage, int totalRecords){
        int totalPage = 0;
               
        if(totalRecords % everyPage == 0)
            totalPage = totalRecords / everyPage;
        else
            totalPage = totalRecords / everyPage + 1 ;
               
        return totalPage;
    }
   
    privatestaticboolean hasPrePage(int currentPage){
        return currentPage == 1 ? false : true;
    }
   
    privatestaticboolean hasNextPage(int currentPage, int totalPage){
        return currentPage == totalPage || totalPage == 0 ? false : true;
    }
   

}



上面的这两个对象与具体的业务逻辑无关,可以独立和抽象。

面对一个具体的业务逻辑:分页查询出User,每页10个结果。具体做法如下:
1. 编写一个通用的结果存储类Result,这个类包含一个Page对象的信息,和一个结果集List:
java代码: 


/*Created on 2005-6-13*/
package com.adt.bo;

import java.util.List;

import org.flyware.util.page.Page;

/**
* @author Joa
*/

publicclass Result {

    private Page page;

    privateList content;

    /**
     * The default constructor
     */

    public Result(){
        super();
    }

    /**
     * The constructor using fields
     *
     * @param page
     * @param content
     */

    public Result(Page page, List content){
        this.page = page;
        this.content = content;
    }

    /**
     * @return Returns the content.
     */

    publicList getContent(){
        return content;
    }

    /**
     * @return Returns the page.
     */

    public Page getPage(){
        return page;
    }

    /**
     * @param content
     *            The content to set.
     */

    publicvoid setContent(List content){
        this.content = content;
    }

    /**
     * @param page
     *            The page to set.
     */

    publicvoid setPage(Page page){
        this.page = page;
    }
}



2. 编写业务逻辑接口,并实现它(UserManager, UserManagerImpl)
java代码: 


/*Created on 2005-7-15*/
package com.adt.service;

import net.sf.hibernate.HibernateException;

import org.flyware.util.page.Page;

import com.adt.bo.Result;

/**
* @author Joa
*/

publicinterface UserManager {
   
    public Result listUser(Page page)throws HibernateException;

}



java代码: 


/*Created on 2005-7-15*/
package com.adt.service.impl;

import java.util.List;

import net.sf.hibernate.HibernateException;

import org.flyware.util.page.Page;
import org.flyware.util.page.PageUtil;

import com.adt.bo.Result;
import com.adt.dao.UserDAO;
import com.adt.exception.ObjectNotFoundException;
import com.adt.service.UserManager;

/**
* @author Joa
*/

publicclass UserManagerImpl implements UserManager {
   
    private UserDAO userDAO;

    /**
     * @param userDAO The userDAO to set.
     */

    publicvoid setUserDAO(UserDAO userDAO){
        this.userDAO = userDAO;
    }
   
    /* (non-Javadoc)
     * @see com.adt.service.UserManager#listUser(org.flyware.util.page.Page)
     */

    public Result listUser(Page page)throws HibernateException, ObjectNotFoundException {
        int totalRecords = userDAO.getUserCount();
        if(totalRecords == 0)
            throw new ObjectNotFoundException("userNotExist");
        page = PageUtil.createPage(page, totalRecords);
        List users = userDAO.getUserByPage(page);
        returnnew Result(page, users);
    }

}



其中,UserManagerImpl中调用userDAO的方法实现对User的分页查询,接下来编写UserDAO的代码:
3. UserDAO 和 UserDAOImpl:
java代码: 


/*Created on 2005-7-15*/
package com.adt.dao;

import java.util.List;

import org.flyware.util.page.Page;

import net.sf.hibernate.HibernateException;

/**
* @author Joa
*/

publicinterface UserDAO extends BaseDAO {
   
    publicList getUserByName(String name)throws HibernateException;
   
    publicint getUserCount()throws HibernateException;
   
    publicList getUserByPage(Page page)throws HibernateException;

}



java代码: 


/*Created on 2005-7-15*/
package com.adt.dao.impl;

import java.util.List;

import org.flyware.util.page.Page;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;

import com.adt.dao.UserDAO;

/**
* @author Joa
*/

publicclass UserDAOImpl extends BaseDAOHibernateImpl implements UserDAO {

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserByName(java.lang.String)
     */

    publicList 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();
    }

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserCount()
     */

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

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserByPage(org.flyware.util.page.Page)
     */

    publicList 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();
    }

}



至此,一个完整的分页程序完成。前台的只需要调用userManager.listUser(page)即可得到一个Page对象和结果集对象的综合体,而传入的参数page对象则可以由前台传入,如果用webwork,甚至可以直接在配置文件中指定。

下面给出一个webwork调用示例:
java代码: 


/*Created on 2005-6-17*/
package com.adt.action.user;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.flyware.util.page.Page;

import com.adt.bo.Result;
import com.adt.service.UserService;
import com.opensymphony.xwork.Action;

/**
* @author Joa
*/

publicclass ListUser implementsAction{

    privatestaticfinal Log logger = LogFactory.getLog(ListUser.class);

    private UserService userService;

    private Page page;

    privateList users;

    /*
     * (non-Javadoc)
     *
     * @see com.opensymphony.xwork.Action#execute()
     */

    publicString execute()throwsException{
        Result result = userService.listUser(page);
        page = result.getPage();
        users = result.getContent();
        return SUCCESS;
    }

    /**
     * @return Returns the page.
     */

    public Page getPage(){
        return page;
    }

    /**
     * @return Returns the users.
     */

    publicList getUsers(){
        return users;
    }

    /**
     * @param page
     *            The page to set.
     */

    publicvoid setPage(Page page){
        this.page = page;
    }

    /**
     * @param users
     *            The users to set.
     */

    publicvoid setUsers(List users){
        this.users = users;
    }

    /**
     * @param userService
     *            The userService to set.
     */

    publicvoid setUserService(UserService userService){
        this.userService = userService;
    }
}



上面的代码似乎看不出什么地方设置了page的相关初值,事实上,可以通过配置文件来进行配置,例如,我想每页显示10条记录,那么只需要:
java代码: 


<?xml version="1.0"?>
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
       
        <package name="user" extends="webwork-interceptors">
               
                <!-- The default interceptor stack name -->
        <default-interceptor-ref name="myDefaultWebStack"/>
               
                <action name="listUser" class="com.adt.action.user.ListUser">
                        <param name="page.everyPage">10</param>
                        <result name="success">/user/user_list.jsp</result>
                </action>
               
        </package>

</xwork>




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

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




我写的一个用于分页的类,用了泛型了,hoho

java代码: 


package com.intokr.util;

import java.util.List;

/**
* 用于分页的类<br>
* 可以用于传递查询的结果也可以用于传送查询的参数<br>
*
* @version 0.01
* @author cheng
*/

publicclass Paginator<E> {
        privateint count = 0; // 总记录数
        privateint p = 1; // 页编号
        privateint num = 20; // 每页的记录数
        privateList<E> results = null; // 结果

        /**
        * 结果总数
        */

        publicint getCount(){
                return count;
        }

        publicvoid setCount(int count){
                this.count = count;
        }

        /**
        * 本结果所在的页码,从1开始
        *
        * @return Returns the pageNo.
        */

        publicint getP(){
                return p;
        }

        /**
        * if(p<=0) p=1
        *
        * @param p
        */

        publicvoid setP(int p){
                if(p <= 0)
                        p = 1;
                this.p = p;
        }

        /**
        * 每页记录数量
        */

        publicint getNum(){
                return num;
        }

        /**
        * if(num<1) num=1
        */

        publicvoid setNum(int num){
                if(num < 1)
                        num = 1;
                this.num = num;
        }

        /**
        * 获得总页数
        */

        publicint getPageNum(){
                return(count - 1) / num + 1;
        }

        /**
        * 获得本页的开始编号,为 (p-1)*num+1
        */

        publicint getStart(){
                return(p - 1) * num + 1;
        }

        /**
        * @return Returns the results.
        */

        publicList<E> getResults(){
                return results;
        }

        publicvoid setResults(List<E> results){
                this.results = results;
        }

        publicString toString(){
                StringBuilder buff = new StringBuilder();
                buff.append("{");
                buff.append("count:").append(count);
                buff.append(",p:").append(p);
                buff.append(",nump:").append(num);
                buff.append(",results:").append(results);
                buff.append("}");
                return buff.toString();
        }

}



感谢大家的分享
尤其是看到了 Projections 的咚咚
以前用Criteria的时候就是没法做count才改用HQL,现在能够这样做的话,还是相当理想的。
我看简单应用的后台分页部分这样基本上也算是圆满了,如果用tapestry,再构造一个支持后台数据库分页,排序的东西,将会带来极大的方便,嘿嘿


今天看Robin的代码,测试了一下,发现一个问题。
public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, final int pageSize,
final int startIndex)
这里如果detachedCriteria中,加入了Order对象,在函数中,计算
int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
criteria.setProjection(null);
会报告异常,指示Order对象有问题。
假设查询语句为:
from Person where type = 2 order by logTime

在计算总个数时,Hibernate输出的SQL语句是
select count(*) from Person where type = 2 order by logTime
语法出错。

是否在函数中,将Order对象单独处理,作为参数传入。
public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, Order order,final int pageSize,
final int startIndex){
return (PaginationSupport) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
criteria.setProjection(null);
criteria.addOrder(order);
List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
PaginationSupport ps = new PaginationSupport(items, totalCount, pageSize, startIndex);
return ps;
}
}, true);
}


robbin 写道:
Hibernate3提供了DetachedCriteria,使得我们可以在Web层构造detachedCriteria,然后调用业务层Bean,进行动态条件查询,根据这一功能,我设计了通用的抽象Bean基类和分页类支持,代码来自于Quake Wang的javaeye-core包的相应类,然后又做了很多修改。

分页支持类:

java代码: 


package com.javaeye.common.util;

import java.util.List;

publicclass PaginationSupport {

        publicfinalstaticint PAGESIZE = 30;

        privateint pageSize = PAGESIZE;

        privateList items;

        privateint totalCount;

        privateint[] indexes = newint[0];

        privateint startIndex = 0;

        public PaginationSupport(List items, int totalCount){
                setPageSize(PAGESIZE);
                setTotalCount(totalCount);
                setItems(items);               
                setStartIndex(0);
        }

        public PaginationSupport(List items, int totalCount, int startIndex){
                setPageSize(PAGESIZE);
                setTotalCount(totalCount);
                setItems(items);               
                setStartIndex(startIndex);
        }

        public PaginationSupport(List items, int totalCount, int pageSize, int startIndex){
                setPageSize(pageSize);
                setTotalCount(totalCount);
                setItems(items);
                setStartIndex(startIndex);
        }

        publicList getItems(){
                return items;
        }

        publicvoid setItems(List items){
                this.items = items;
        }

        publicint getPageSize(){
                return pageSize;
        }

        publicvoid setPageSize(int pageSize){
                this.pageSize = pageSize;
        }

        publicint getTotalCount(){
                return totalCount;
        }

        publicvoid setTotalCount(int totalCount){
                if(totalCount > 0){
                        this.totalCount = totalCount;
                        int count = totalCount / pageSize;
                        if(totalCount % pageSize > 0)
                                count++;
                        indexes = newint[count];
                        for(int i = 0; i < count; i++){
                                indexes[i] = pageSize * i;
                        }
                }else{
                        this.totalCount = 0;
                }
        }

        publicint[] getIndexes(){
                return indexes;
        }

        publicvoid setIndexes(int[] indexes){
                this.indexes = indexes;
        }

        publicint getStartIndex(){
                return startIndex;
        }

        publicvoid setStartIndex(int startIndex){
                if(totalCount <= 0)
                        this.startIndex = 0;
                elseif(startIndex >= totalCount)
                        this.startIndex = indexes[indexes.length - 1];
                elseif(startIndex < 0)
                        this.startIndex = 0;
                else{
                        this.startIndex = indexes[startIndex / pageSize];
                }
        }

        publicint getNextIndex(){
                int nextIndex = getStartIndex() + pageSize;
                if(nextIndex >= totalCount)
                        return getStartIndex();
                else
                        return nextIndex;
        }

        publicint getPreviousIndex(){
                int previousIndex = getStartIndex() - pageSize;
                if(previousIndex < 0)
                        return0;
                else
                        return previousIndex;
        }

}



抽象业务类
java代码: 


/**
* Created on 2005-7-12
*/

package com.javaeye.common.business;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.javaeye.common.util.PaginationSupport;

publicabstractclass AbstractManager extends HibernateDaoSupport {

        privateboolean cacheQueries = false;

        privateString queryCacheRegion;

        publicvoid setCacheQueries(boolean cacheQueries){
                this.cacheQueries = cacheQueries;
        }

        publicvoid setQueryCacheRegion(String queryCacheRegion){
                this.queryCacheRegion = queryCacheRegion;
        }

        publicvoid save(finalObject entity){
                getHibernateTemplate().save(entity);
        }

        publicvoid persist(finalObject entity){
                getHibernateTemplate().save(entity);
        }

        publicvoid update(finalObject entity){
                getHibernateTemplate().update(entity);
        }

        publicvoid delete(finalObject entity){
                getHibernateTemplate().delete(entity);
        }

        publicObject load(finalClass entity, finalSerializable id){
                return getHibernateTemplate().load(entity, id);
        }

        publicObject get(finalClass entity, finalSerializable id){
                return getHibernateTemplate().get(entity, id);
        }

        publicList findAll(finalClass entity){
                return getHibernateTemplate().find("from " + entity.getName());
        }

        publicList findByNamedQuery(finalString namedQuery){
                return getHibernateTemplate().findByNamedQuery(namedQuery);
        }

        publicList findByNamedQuery(finalString query, finalObject parameter){
                return getHibernateTemplate().findByNamedQuery(query, parameter);
        }

        publicList findByNamedQuery(finalString query, finalObject[] parameters){
                return getHibernateTemplate().findByNamedQuery(query, parameters);
        }

        publicList find(finalString query){
                return getHibernateTemplate().find(query);
        }

        publicList find(finalString query, finalObject parameter){
                return getHibernateTemplate().find(query, parameter);
        }

        public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria){
                return findPageByCriteria(detachedCriteria, PaginationSupport.PAGESIZE, 0);
        }

        public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, finalint startIndex){
                return findPageByCriteria(detachedCriteria, PaginationSupport.PAGESIZE, startIndex);
        }

        public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, finalint pageSize,
                        finalint startIndex){
                return(PaginationSupport) getHibernateTemplate().execute(new HibernateCallback(){
                        publicObject doInHibernate(Session session)throws HibernateException {
                                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();
                                PaginationSupport ps = new PaginationSupport(items, totalCount, pageSize, startIndex);
                                return ps;
                        }
                }, true);
        }

        publicList findAllByCriteria(final DetachedCriteria detachedCriteria){
                return(List) getHibernateTemplate().execute(new HibernateCallback(){
                        publicObject doInHibernate(Session session)throws HibernateException {
                                Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                                return criteria.list();
                        }
                }, true);
        }

        publicint getCountByCriteria(final DetachedCriteria detachedCriteria){
                Integer count = (Integer) getHibernateTemplate().execute(new HibernateCallback(){
                        publicObject doInHibernate(Session session)throws HibernateException {
                                Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                                return criteria.setProjection(Projections.rowCount()).uniqueResult();
                        }
                }, true);
                return count.intValue();
        }
}




用户在web层构造查询条件detachedCriteria,和可选的startIndex,调用业务bean的相应findByCriteria方法,返回一个PaginationSupport的实例ps。

ps.getItems()得到已分页好的结果集
ps.getIndexes()得到分页索引的数组
ps.getTotalCount()得到总结果数
ps.getStartIndex()当前分页索引
ps.getNextIndex()下一页索引
ps.getPreviousIndex()上一页索引





重要的 page的算法
http://pear.php.net/package/Pager

而不是 如何从数据库中 获取分页的数据

最好同时提供测试代码



我个人认为,如果在Web层或业务层构造detachedCriteria,会造成整个应用对hibernate的依赖,限定了data access一定要由hibernate实现,如果想要替换成其他实现就会牵涉到多个层的改动。
问题:
有没有其他的解决方案呢?


引用:
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......




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(可前几次都返回了正确的结果)。

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


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(可前几次都返回了正确的结果)。

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



1:具体的说是单表查询可以,但是如果关联其它表,或者createAlias,返回的items就会有问题,掉换顺序也不行,items里面的每个元素都是一个数组。我的解决办法是再加一个ResultTransformer参数,在setProjection(null)后再setResultTransformer

DetachedCriteria确实实现的不是很完善,我们希望它是一个无状态的,仅保存查询条件的值对象,但实际上它做不到。一个DetachedCriteria对象反复做分页查询,第一次查询时调用的setMaxResults方法和setFirstResult方法后,这个状态保存在DetachedCriteria上了,会影响下一次count操作,因此每次查询必需new一个DetachedCriteria。同样的原因导致第一个问题交换操作顺序也不行。

经典啊!!感谢大家分享这么多!
只不过有些地方看得不是太懂!
需要自己复制下来测一下。。
如果能把测试类能写出来就更好了~~再次谢谢大家。


看了robbin和downpour的文章,感觉非常棒,我就拿来试试.
个人觉得DetachedCriteria的动态查询(QBC)还是优于HQL的,所以觉得采用QBC来进行查询.但是downpour的层次性好,而且把page分页类的初始化和操作分开,将页面对象化,那么涉及到页面的时候只要传一个page对象,至于内部page是如何的可以不用管了,又更好的封装性.但是从算法而言,两位牛人的应该是一样的.
所以我想把两个结合一下,利用downpour GG的层次结构,用QBC(DetachedCriteria)查询条件...前面的原封不动,只要在DAO实现这里修改一下就行了.但是具体实现有问题,我是个初学者,对这些东西的原理还不是很清楚.
java代码: 

/*Created on 2005-7-15*/
package com.adt.dao.impl;

import java.util.List;

import org.flyware.util.page.Page;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;

import com.adt.dao.UserDAO;

/**
* @author Joa
*/

publicclass UserDAOImpl extends BaseDAOHibernateImpl implements UserDAO {

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserByName(java.lang.String)
     */

    publicList 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();
    }

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserCount()
     */

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

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserByPage(org.flyware.util.page.Page)
     */

    publicList 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();
    }

}



下面是我写的一点,把上面的HQL转换成QBC..很明显有问题,希望懂的达人帮忙给我看看
java代码: 

    publicList getStudentByPage(Page page)throws HibernateException {
            //HQL实现
        String querySentence = "FROM user in class com.adt.po.User";
        Query query = getSession().createQuery(querySentence);
           
        //DetachedCriteria实现
                //处理JSF参数
                studentId = FacesUtils.getRequestParameter("query:stuId");
                studentName = FacesUtils.getRequestParameter("query:stuName");
                studentSex = FacesUtils.getRequestParameter("query:sex");
                studentDegree = FacesUtils.getRequestParameter("query:degree");
                studentProvince = FacesUtils.getRequestParameter("query:province");
               

                this.logger.debug("------------studentName----> "+ studentId);
                this.logger.debug("------------studentName----> "+ studentName);
                this.logger.debug("------------studentName----> "+ studentSex);
                this.logger.debug("------------studentName----> "+ studentDegree);
                this.logger.debug("------------studentName----> "+ studentProvince);
               
                //设置页面
                int pageSize = 10;
                int startIndex = 0;
               
                //构造DetachedCriteria
                DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Student.class);
                detachedCriteria.add(Restrictions.eq("stuName", studentName));
               
                Criteria criteria = null;//这里不知道怎么写,肯定不是这样写的
               
                criteria.setFirstResult(page.getBeginIndex())
                        .setMaxResults(page.getEveryPage());
               
                return criteria.list();
    }


java代码: 

                Criteria criteria = null;//这里该怎么写???在看到的资料都要涉及到session...DetachedCriteria好像没setFirstResult和setMaxResults..如何讲Criteria和DetachedCriteria联系起来,还是有其他的方法写这个???



上一次由pedestrian_I于2005-8-30 周二, 下午3:37修改,总共修改了2次




文章 时间: 2005-8-30 11:24:30    标题: 引用回复 将这个帖子加入我的Blog

我也用了robbin的方法来做,直接QBC查询
在bean里面的action里实现分页
java代码: 

public String queryAction(){
                this.logger.debug("get into queryAction!!!!!!!!!");
               
                try{

                        //处理JSF参数
                        studentId = FacesUtils.getRequestParameter("query:stuId");
                        studentName = FacesUtils.getRequestParameter("query:stuName");
                        studentSex = FacesUtils.getRequestParameter("query:sex");
                        studentDegree = FacesUtils.getRequestParameter("query:degree");
                        studentProvince = FacesUtils.getRequestParameter("query:province");
                       
       
                        this.logger.debug("------------studentName----> "+ studentId);
                        this.logger.debug("------------studentName----> "+ studentName);
                        this.logger.debug("------------studentName----> "+ studentSex);
                        this.logger.debug("------------studentName----> "+ studentDegree);
                        this.logger.debug("------------studentName----> "+ studentProvince);
                       
                        //设置页面
                        int pageSize = 10;
                        int startIndex = 0;
                       
                        //构造DetachedCriteria
                        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Student.class);
                        detachedCriteria.add(Restrictions.eq("stuName", studentName));
                       
//                        //直接查询
//                        PaginationSupport ps = AbstractManager.findPageByCriteria(DetachedCriteria
//                                        .forClass(Student.class), PageUtil.getEveryPage(pageSize), PageUtil.getBeginIndex(startIndex));
       
                        PaginationSupport ps = AbstractManager.findPageByCriteria(detachedCriteria, pageSize, startIndex);
                       

                       
                        ps.getItems();                     //得到已分页º玫慕峁¯
                        ps.getIndexes();                //得到分页索引的数组
                        ps.getTotalCount();                //得到总结果数
                        ps.getStartIndex();                //当前分页索引
                        ps.getNextIndex();                //下一页索引
                        ps.getPreviousIndex();        //上一页索引
                       

                       
                       
                        //调用service
                        List listStu = this.serviceLocator.getStudentService().getStudents(detachedCriteria);
                        //List listStu = this.serviceLocator.getStudentService().findStudent(stuId,stuName);
                       
//                        this.logger.debug("listStu.size() = "+ listStu.size() );
                       
                        //处理返回的纪录集
                        for(int i=0; i<listStu.size(); i++){
                                Student stu = (Student)listStu.get(i);
                                StudentBean studentBean = StudentBuilder.createStudentBean(stu);
                                studentBean.setServiceLocator(this.serviceLocator);
                                this.studentBeans.add(studentBean);
                                this.logger.debug("listStu==i= "+i );
                    }       
               
                }catch(AppException ce){
                        String msg = "Could not retrieve Students " ;
                        this.logger.debug(msg, ce);
                       
                        throw new FacesException(msg, ce);
                }
                return "QUERY_SUCCESS" ;
        }



java代码: 


                        PaginationSupport ps = AbstractManager.findPageByCriteria(detachedCriteria, pageSize, startIndex);

这里说返回类型不对,static和non-static..应该是返回的类型不对吧...

这个有问题吗??有什么不规范和不对的地方吗????



java代码: 



/**
* 分页信息接口
*/

public interface Page <E>extendsSerializable,Iterable
{
    /**
     * 是否是首页(第一页),第一页页码为1
     *
     * @return 首页标识
     */

    publicboolean isFirstPage();

    /**
     * 是否是最后一页
     *
     * @return 末页标识
     */

    publicboolean isLastPage();

    /**
     * 是否有下一页
     *
     * @return 下一页标识
     */

    publicboolean hasNextPage();

    /**
     * 是否有上一页
     *
     * @return 上一页标识
     */

    publicboolean hasPreviousPage();

    /**
     * 获取最后一页页码,也就是总页数
     *
     * @return 最后一页页码
     */

    publicint getLastPageNumber();

    /**
     * 当前页包ºÄ数据,不Í那榭隹赡芊祷Ø的数据类型不一样,如List,RowSet等,请参考具体的实现
     *
     * @return 当前页数据源
     */

    public E getThisPageElements();

    /**
     * 总的数据条目数量,0表示没有数据
     *
     * @return 总数量
     */

    publicint getTotalNumberOfElements();

    /**
     * 获取当前页的首条数据的行编码
     *
     * @return 当前页的首条数据的行编码
     */

    publicint getThisPageFirstElementNumber();

    /**
     * 获取当前页的末条数据的行编码
     *
     * @return 当前页的末条数据的行编码
     */

    publicint getThisPageLastElementNumber();

    /**
     * 获取下一页编码
     *
     * @return 下一页编码
     */

    publicint getNextPageNumber();

    /**
     * 获取上一页编码
     *
     * @return 上一页编码
     */

    publicint getPreviousPageNumber();

    /**
     * 每一页显示的条目数
     *
     * @return 每一页显示的条目数
     */

    publicint getPageSize();

    /**
     * 当前页的页码
     *
     * @return 当前页的页码
     */

    publicint getThisPageNumber();

}



BasePage用于其它的page子类继承
java代码: 



/**
* 高层的Page接口实现
*
* @author QIU
*/

public class BasePage<E> implements Page {

        protected E elements;

        protectedint pageSize;

        protectedint pageNumber;

        protectedint totalElements = 0;

        public BasePage(int pageNumber, int pageSize, int totalElements){
                this.pageNumber = pageNumber;
                this.pageSize = pageSize;
                this.totalElements = totalElements;
                recomputePageNumber();
        }

        public BasePage(int pageNumber, int pageSize){
                this.pageNumber = pageNumber;
                this.pageSize = pageSize;
        }

        /**
        * 重新计Ë愕鼻耙³的号码
        */

        protectedvoid recomputePageNumber(){
                if(Integer.MAX_VALUE == this.pageNumber
                                || this.pageNumber > getLastPageNumber()){//last page
                        this.pageNumber = getLastPageNumber();
                }
        }

        publicboolean isFirstPage(){
                return getThisPageNumber() == 1;
        }

        publicboolean isLastPage(){
                return getThisPageNumber() >= getLastPageNumber();
        }

        publicboolean hasNextPage(){
                return getLastPageNumber() > getThisPageNumber();
        }

        publicboolean hasPreviousPage(){
                return getThisPageNumber() > 1;
        }

        publicint getLastPageNumber(){
                return totalElements % this.pageSize == 0 ?
                                totalElements/ this.pageSize
                                : totalElements / this.pageSize + 1;
        }

        public E getThisPageElements(){
                return elements;
        }

        publicint getTotalNumberOfElements(){
                return totalElements;
        }

        publicint getThisPageFirstElementNumber(){
                return(getThisPageNumber() - 1) * getPageSize() + 1;
        }

        publicint getThisPageLastElementNumber(){
                int fullPage = getThisPageFirstElementNumber() + getPageSize() - 1;
                return getTotalNumberOfElements() < fullPage ? getTotalNumberOfElements() : fullPage;
        }

        publicint getNextPageNumber(){
                return getThisPageNumber() + 1;
        }

        publicint getPreviousPageNumber(){
                return getThisPageNumber() - 1;
        }

        publicint getPageSize(){
                return pageSize;
        }

        publicint getThisPageNumber(){
                return pageNumber;
        }
        /**
        * 实现Iterable遍历接口
        * @return 等价((Iterable)getThisPageElements()).iterator()
        * @throws UnsupportedOperationException
        *                    当getThisPageElements()不是Iterable接口
        * @See java.lang.Iterable
        */

        publicIterator iterator(){
                if(getThisPageElements() instanceof Iterable){
                        return((Iterable)getThisPageElements()).iterator();
                }
                else{
                        throw newUnsupportedOperationException("iterator() method is unsupported");
                }
        }
       

}



现在我们的HibernatePage实现类,只需简单的执行一下查询就行了
java代码: 


/**
* Hibernate分页信息
* @see BasePage
*/

public class HibernatePage extends BasePage implements Page
{

    /**
     * 构建HibernatePage对象,完成Hibernate的Query数据的分页处理
     *
     * @param query      Hibernate的Query对象
     * @param pageNumber 当前页编码,从1开始,如果传的值为Integer.MAX_VALUE表示获取最后一页。
     *                   如果你不知道最后一页编码,传Integer.MAX_VALUE即可。如果当前页超过总页数,也表示最后一页。
     *                  犝饬街智榭鼋匦赂Ä当前页的页码,为最后一页编码。
     * @param pageSize  犆¿一页显示的条目数
     */

    public HibernatePage(Query query, int pageNumber, int pageSize)
    {
        super(pageNumber,pageSize);
        try
        {
            ScrollableResults scrollableResults = query.scroll();
            //get the total elements number
            scrollableResults.last();
            this.totalElements = scrollableResults.getRowNumber();
            recomputePageNumber();
            elements = query.setFirstResult((this.pageNumber - 1) * this.pageSize).setMaxResults(this.pageSize + 1).list();
        }catch(HibernateException e)
        {
            throw newRuntimeException(e);
        }
    }
   
    /**
     * 构建HibernatePage对象,完成Hibernate的Query数据的分页处理
     *
     * @param selectQuery              Hibernate的查询Query对象
     * @param countQuery                Hibernate的查询总数据行数的Query对象
     * @param pageNumber 当前页编码,从1开始,如果传的值为Integer.MAX_VALUE表示获取最后一页。
     *                   如果你不知道最后一页编码,传Integer.MAX_VALUE即可。如果当前页超过总页数,也表示最后一页。
     *                  犝饬街智榭鼋匦赂Ä当前页的页码,为最后一页编码。
     * @param pageSize  犆¿一页显示的条目数
     */

    public HibernatePage(Query selectQuery, Query countQuery,int pageNumber, int pageSize)
    {
            super(pageNumber,pageSize);
        try
        {
            //得到总记录数
            this.totalElements = ((Integer)countQuery.uniqueResult()).intValue();
            recomputePageNumber();
            elements = selectQuery
                    .setFirstResult((this.pageNumber - 1) * this.pageSize)
                    .setMaxResults(this.pageSize + 1)
                    .list();
           
        }catch(HibernateException e)
        {
            throw newRuntimeException(e);
        }
    }
}



EJB3.0的Page接口实现EJBPage
java代码: 


/**
* EJB分页信息
* @author QIU
* @see BasePage
*/

public class EJBPage extends BasePage {
       
        public EJBPage(Query selectQuery,Query countQuery,int pageNumber,int pageSize){
               
                super(pageNumber,pageSize);
                this.totalElements = (Integer)countQuery.getSingleResult();
                recomputePageNumber();
                this.elements = selectQuery
                        .setFirstResult((this.pageNumber - 1) * this.pageSize)
                        .setMaxResults(this.pageSize + 1)
                        .getResultList();
        }
       
}

JDBCPage

/**
* Jdbc分页信息,需要Jdk 1.5的支持
* @see Page
* @see BasePage
*/
public class JdbcPage extends BasePage //implements Page
{
// private CachedRowSet elements;
// private int pageSize;
// private int pageNumber;
// private int totalElements = 0;

/**
* 构建JdbcPage对象,完成JDBC的ResultSet分页处理
*
* @param rs ResultSet对象,ResultSet必须是可滚动的数据集,可以通过以下的Statement执行查询。
* Statement stmt = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
* @param pageNumber 当前页编码,从1开始,如果传的值为Integer.MAX_VALUE表示获取最后一页。
* 如果你不知道最后一页编码,传Integer.MAX_VALUE即可。如果当前页超过总页数,也表示最后一页。
* 这两种情况将重新更改当前页的页码,为最后一页编码。
* @param pageSize 每一页显示的条目数
*/
public JdbcPage(ResultSet rs, int pageNumber, int pageSize)
{
super(pageNumber,pageSize);
try
{
//get the total elements number
rs.last();
totalElements = rs.getRow();
// if (this.pageNumber == Integer.MAX_VALUE || this.pageNumber > getLastPageNumber())
// {
// this.pageNumber = getLastPageNumber();
// }
recomputePageNumber();
CachedRowSet elements = new CachedRowSetImpl();
elements.setMaxRows(this.pageSize);
elements.setPageSize(this.pageSize);
elements.populate(rs, (this.pageNumber - 1) * this.pageSize);
this.elements = elements;

} catch (SQLException e)
{
throw new RuntimeException(e);
}
}

/**
* 构建JdbcPage对象,完成JDBC的ResultSet分页处理
*
* @param rs ResultSet对象,ResultSet必须是可滚动的数据集,可以通过以下的Statement执行查询。
* Statement stmt = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
* @param pageNumber 当前页编码,从1开始,如果传的值为Integer.MAX_VALUE表示获取最后一页。
* 如果你不知道最后一页编码,传Integer.MAX_VALUE即可。如果当前页超过总页数,也表示最后一页。
* 这两种情况将重新更改当前页的页码,为最后一页编码。
* @param pageSize 每一页显示的条目数
*/
public JdbcPage(ResultSet rs,BusinessObjectCreator creator, int pageNumber, int pageSize)
{
super(pageNumber,pageSize);
try
{
//get the total elements number
rs.last();
totalElements = rs.getRow();
recomputePageNumber();

this.elements = populateIntoList(rs,creator,pageNumber,pageSize);

} catch (SQLException e){
throw new RuntimeException(e);
}
}

private static List populateIntoList(ResultSet rs, BusinessObjectCreator factory, int pageNumber, int pageSize) throws SQLException {
List result = new ArrayList();
//移动到页码的第一条记录,忽略第一页
if(pageNumber>1){
rs.absolute(((pageNumber-1)*pageSize));
}
for(int i=0;i<pageSize;i++){
if(rs.next()){
result.add(factory.getBusinessObject(rs));
} else{
if(i==0){ //没有记录的情况
return null;
}
break;
}
}
return result;
}
}


BusinessObjectCreator接口
java代码: 


/**
* 业务对象创建接口,用于JDBCPage创建对象方法回调
* @author QIU
*
*/

public interface BusinessObjectCreator {
        publicObject getBusinessObject(ResultSet rs);
}

java代码: 


/**
* Hibernate使用Criteria分页信息
* @author QIU
*
*/

public class CriteriaPage extends BasePage{

        public CriteriaPage(Criteria criteria,int pageNumber, int pageSize){
                super(pageNumber,pageSize);
                //得到总记录数
                ScrollableResults scrollableResults = criteria.scroll();
                scrollableResults.last();
                this.totalElements = scrollableResults.getRowNumber();
               
                recomputePageNumber();
                elements = criteria
                        .setFirstResult((this.pageNumber - 1) * this.pageSize)
                        .setMaxResults(this.pageSize + 1).list();
        }

}



重贴JdbcPage
java代码: 



/**
* Jdbc分页信息,需要Jdk 1.5的支持
* @see Page
* @see BasePage
*/

public class JdbcPage extends BasePage //implements Page
{
//    private CachedRowSet elements;
//    private int pageSize;
//    private int pageNumber;
//    private int totalElements = 0;

    /**
     * 构建JdbcPage对象,完成JDBC的ResultSet分页处理
     *
     * @param rs         ResultSet对象,ResultSet必须是可滚动的数据集,可以通过Ò韵Â的Statement执行查询。
     *                   Statement stmt = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
     * @param pageNumber 当前页编码,从1开始,如果传的值为Integer.MAX_VALUE表示获取最后一页。
     *                   如果你不ÖÀ最后一页编码,传Integer.MAX_VALUE即可。如果当前页超过总页数,也表示最后一页。
     *                  犝饬街智榭鼋匦赂Ä当前页的页码,为最后一页编码。
     * @param pageSize  犆¿一页显示的条目数
     */

    public JdbcPage(ResultSet rs, int pageNumber, int pageSize)
    {
            super(pageNumber,pageSize);
        try
        {
            //get the total elements number
            rs.last();
            totalElements = rs.getRow();
//            if (this.pageNumber == Integer.MAX_VALUE || this.pageNumber > getLastPageNumber())
//            {
//                this.pageNumber = getLastPageNumber();
//            }
            recomputePageNumber();
            CachedRowSet elements = new CachedRowSetImpl();
            elements.setMaxRows(this.pageSize);
            elements.setPageSize(this.pageSize);
            elements.populate(rs, (this.pageNumber - 1) * this.pageSize);
            this.elements = elements;
           
        }catch(SQLException e)
        {
            throw newRuntimeException(e);
        }
    }
   
    /**
     * 构建JdbcPage对象,完成JDBC的ResultSet分页处理
     *
     * @param rs         ResultSet对象,ResultSet必须是可滚动的数据集,可以通过Ò韵Â的Statement执行查询。
     *                   Statement stmt = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
     * @param pageNumber 当前页编码,从1开始,如果传的值为Integer.MAX_VALUE表示获取最后一页。
     *                   如果你不ÖÀ最后一页编码,传Integer.MAX_VALUE即可。如果当前页超过总页数,也表示最后一页。
     *                  犝饬街智榭鼋匦赂Ä当前页的页码,为最后一页编码。
     * @param pageSize  犆¿一页显示的条目数
     */

    public JdbcPage(ResultSet rs,BusinessObjectCreator creator, int pageNumber, int pageSize)
    {
            super(pageNumber,pageSize);
        try
        {
            //get the total elements number
            rs.last();
            totalElements = rs.getRow();
            recomputePageNumber();
           
            this.elements = populateIntoList(rs,creator,pageNumber,pageSize);
           
        }catch(SQLException e){
            throw newRuntimeException(e);
        }
    }
   
        privatestaticList populateIntoList(ResultSet rs, BusinessObjectCreator factory, int pageNumber, int pageSize)throwsSQLException{
                List result = newArrayList();
                //移动到页码的第一条记录,忽略第一页
                if(pageNumber>1){
                      rs.absolute(((pageNumber-1)*pageSize));
                }
                for(int i=0;i<pageSize;i++){
                      if(rs.next()){
                        result.add(factory.getBusinessObject(rs));
                      }else{
                        if(i==0){//没有记录的情况
                                returnnull;
                        }
                        break;
                      }
                   }
                return result;
        }
}



java代码: 


/**
* 业务对象创建接口,用于JDBCPage创建对象方法回调
* @author QIU
*
*/

public interface BusinessObjectCreator {
        publicObject getBusinessObject(ResultSet rs);
}



其它的Page接口实现,我们只需简单的继承BasePage,轻松的就可以增加对Page的实现.Page接口详细可以查看Hibernate官网上的描述


ScrollableResults scrollableResults = query.scroll();
//get the total elements number
scrollableResults.last();
this.totalElementsCount = scrollableResults.getRowNumber() + 1;
我使用oracle9i + oracle10 jdbc driver, 在数据量大的情况下,会有outofmemery exception的

今天试了一下robbin的分页方法,发现一个问题

方法代码:
java代码: 

public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, finalint pageSize,
                        finalint startIndex){
                return(PaginationSupport) getHibernateTemplate().execute(new HibernateCallback(){
                        publicObject doInHibernate(Session session)throws HibernateException {
                                Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                                int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
                                [b]criteria.setProjection(null);[/b]
                                List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
                                PaginationSupport ps = new PaginationSupport(items, totalCount, pageSize, startIndex);
                                return ps;
                        }
                }, true);
        }



调用代码:
java代码: 


DetachedCriteria dc=DetachedCriteria .forClass(Cat.class);
dc.createAlias("mate","mt")
   .add(Property.forName("mt.name").like("miaomiao",MatchMode.ANYWHERE));
PaginationSupport ps = findPageByCriteria(dc,20,0);
List list = ps.getItems();
[b]Object[] objects = Object[] list.get(0);[/b]



发现返回的List里面放的不是我本来想要的Cat, 而是Object[].

经过测试,看来问题出在 criteria.setProjection(null); 上。

goldapple 写道:
有没有解决方案啊?


java代码: 


package com.skyon.cams.util;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.impl.CriteriaImpl.OrderEntry;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.Assert;

import com.skyon.framework.core.SkyonException;
import com.skyon.framework.dao.PaginationSupport;

/**
* 一些常用的 Hibernate 方法, 注意, 一些方法可能只适用于 Hibernate3.0.5 !!!
* @see org.springframework.orm.hibernate3.HibernateTemplate
* @since 2005-9-13
* @author 王政
* @version $Id: HibernateUtils.java,v 1.4 2005/09/19 01:00:28 wangzheng Exp $
*/

publicabstractclass HibernateUtils {
       
        privatestatic Log logger = LogFactory.getLog(HibernateUtils.class);
       
        publicstaticfinalString CRITERIA_ASSERT_ERROR_MESSAGE = " 's type is not " + CriteriaImpl.class + ", please make sure you are using Hibernate3.0.5!!! ";
       
    /**
     * 将 Criteria 加上分页条件并输出结果
     * @param criteria the criteria
     * @param offset the offset
     * @param maxPageItems the maxPageItems
     * @return the result list
     */
    public static List getPageResult(Criteria criteria, int offset, int maxPageItems) throws HibernateException {
        criteria.setFirstResult(offset);
        criteria.setMaxResults(maxPageItems);
        return criteria.list();
    }
   
        /**
        * 根据 DetachedCriteria 得 到 分页结果, 运行期间会根据 criteria  自动运算总行数, 注意如果 criteria 中 set 了 Projection, 则返回结果 List 中为 Projection 指定类型
        * @param hibernateTemplate        the hibernateTemplate
        * @param criteria the criteria
        * @param firstResult the first result row number
        * @param maxResults the max result
        * @return the pagination support
        * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
        */
        public static PaginationSupport findByCriteria(HibernateTemplate hibernateTemplate, final DetachedCriteria criteria, final int firstResult, final int maxResults)
                throws DataAccessException {
               
                return (PaginationSupport) hibernateTemplate.execute(new HibernateCallback() {
                        public Object doInHibernate(Session session) throws HibernateException, SQLException {
                                Criteria executableCriteria = criteria.getExecutableCriteria(session);
                               
                                // Get the orginal orderEntries
                                OrderEntry[] orderEntries = HibernateUtils.getOrders(executableCriteria);
                                // Remove the orders
                                executableCriteria = HibernateUtils.removeOrders(executableCriteria);                               
                                // get the original projection
                                Projection projection = HibernateUtils.getProjection(executableCriteria);
                               
                int totalCount = ((Integer) executableCriteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
               
                executableCriteria.setProjection(projection);
                if (projection == null) {
                        // Set the ResultTransformer to get the same object structure with hql
                        executableCriteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
                }             
                // Add the orginal orderEntries
                executableCriteria = HibernateUtils.addOrders(executableCriteria, orderEntries);
               
                // Now, the Projection and the orderEntries have been resumed
                List items = HibernateUtils.getPageResult(executableCriteria, firstResult, maxResults);                                               
                                return new PaginationSupport(items, totalCount, firstResult, maxResults);
                        }
                }, true);
        }
       
        /**
        * 根据 hql 得 到 总行数
        * @param hibernateTemplate
        * @param queryString hql
        * @param isNamedQuery 是否是 named query
        * @param paramNames 如果是 named query, 为 named query 中的参数名称, 否则无意义
        * @param paramValues 参数值
        * @return 满足条件的总行数
        * @throws IllegalArgumentException if queryString is blank
        * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
        */
        public static int getTotalCount(HibernateTemplate hibernateTemplate, String queryString, boolean isNamedQuery, String[] paramNames, Object[] paramValues)
                throws IllegalArgumentException, DataAccessException {
                if (StringUtils.isBlank(queryString)) {
                    throw new IllegalArgumentException(" queryString can'
t be blank ");
            }
                       
                String countQueryString = " select count (*) " + JdbcUtils.removeSelect(JdbcUtils.removeOrders(queryString));
               
                List countList;
               
                if(isNamedQuery){
                        countList = hibernateTemplate.findByNamedParam(countQueryString, paramNames, paramValues);                       
                }else{
                        countList = hibernateTemplate.find(countQueryString, paramValues);               
                }
               
                return((Integer) countList.get(0)).intValue();
        }
   
       
    /**
     * 从 criteria 中取得 {@link Projection}, 接口中没有公开此方法, 因此从 {@link CriteriaImpl} 中取得
     * @see CriteriaImpl#getProjection()
     * @param criteria the criteria
     * @return the Projection
     */

    publicstatic Projection getProjection(Criteria criteria){
            assertType(criteria);
            CriteriaImpl impl = (CriteriaImpl) criteria;
            return impl.getProjection();
    }
   
   
        privatestaticvoid assertType(Criteria criteria){
                Assert.notNull(criteria, " criteria is required. ");
                String message = criteria + CRITERIA_ASSERT_ERROR_MESSAGE;
                if(! CriteriaImpl.class.isInstance(criteria)){
                    if(logger.isDebugEnabled()){
                            logger.debug(message);
                    }
                    throw new SkyonException(message);
            }
        }
   
        /**
        * µ玫½ criteria 中的 OrderEntry[]
        * @param criteria the criteria
        * @return the OrderEntry[]
        */

    publicstatic OrderEntry[] getOrders(Criteria criteria){
            assertType(criteria);
                CriteriaImpl impl = (CriteriaImpl) criteria;
                Field field = getOrderEntriesField(criteria);
                try{
                        return(OrderEntry[])((List) field.get(impl)).toArray(new OrderEntry[0]);
                }catch(Exception e){
                    logAndThrowException(criteria, e);
                    throw newInternalError(" RuntimeException impossibility can't throw ");
                }
    }
   
   
        /**
        * 移除 criteria 中的 OrderEntry[]
        * @param criteria the criteria
        * @return the criteria after removed OrderEntry[]
        */
    public static Criteria removeOrders(Criteria criteria) {
            assertType(criteria);
            CriteriaImpl impl = (CriteriaImpl) criteria;
           
            try {
                Field field = getOrderEntriesField(criteria);
                field.set(impl, new ArrayList());
                return impl;
            } catch (Exception e) {
                    logAndThrowException(criteria, e);
                    throw new InternalError(" Runtime Exception impossibility can'
t throw ");
            }       
    }

    /**
     * 为 criteria 增加 OrderEntry[]
     * @param criteria the criteria
     * @param orderEntries the OrderEntry[]
     * @return the criteria after add OrderEntry[]
     */

    publicstatic Criteria addOrders(Criteria criteria, OrderEntry[] orderEntries){
            assertType(criteria);
            CriteriaImpl impl = (CriteriaImpl) criteria;
            try{
                Field field = getOrderEntriesField(criteria);
                for(int i = 0; i < orderEntries.length; i++){
                        List innerOrderEntries = (List) field.get(criteria);
                        innerOrderEntries.add(orderEntries[i]);
                }
                return impl;
            }catch(Exception e){
                    logAndThrowException(criteria, e);
                    throw newInternalError(" RuntimeException impossibility can't throw ");
            }
    }

        private static void logAndThrowException(Criteria criteria, Exception e) {
                String message = criteria + CRITERIA_ASSERT_ERROR_MESSAGE;
                if (logger.isDebugEnabled()) {
                        logger.debug(message, e);
                }
                throw new SkyonException(message, e);
        }
   
        private static Field getOrderEntriesField(Criteria criteria) {
                Assert.notNull(criteria, " criteria is requried. " );
                try {
                        Field field = CriteriaImpl.class.getDeclaredField("orderEntries");
                        field.setAccessible(true);
                        return field;
                } catch (Exception e) {
                        logAndThrowException(criteria, e);
                    throw new InternalError();
                }
        }
}


楼上的,你写的东西恐怕有问题吧。

java代码: 


String countQueryString = " select count (*) " + JdbcUtils.removeSelect(JdbcUtils.removeOrders(queryString));



就看这句,如果有group by呢?


downpour 写道:
楼上的,你写的东西恐怕有问题吧。

java代码: 


String countQueryString = " select count (*) " + JdbcUtils.removeSelect(JdbcUtils.removeOrders(queryString));



就看这句,如果有group by呢?


呵呵, 的确有这问题, 这个方法看来只适用于最简单的查询


downpour 写道:
连续看了两篇robbin有关DetachedCriteria的介绍,感觉真的不错,尤其是上面的示例代码,让我着实觉得该对我原来的分页查询做一下代码重构了。

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

首先,为了实现分页查询,我封装了一个Page类:
java代码: 


/*Created on 2005-4-14*/
package org.flyware.util.page;

/**
* @author Joa
*
*/

publicclass Page {
   
    /** imply if the page has previous page */
    privateboolean hasPrePage;
   
    /** imply if the page has next page */
    privateboolean hasNextPage;
       
    /** the number of every page */
    privateint everyPage;
   
    /** the total page number */
    privateint totalPage;
       
    /** the number of current page */
    privateint currentPage;
   
    /** the begin index of the records by the current query */
    privateint 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.
     */

    publicint getBeginIndex(){
        return beginIndex;
    }
   
    /**
     * @param beginIndex
     * The beginIndex to set.
     */

    publicvoid setBeginIndex(int beginIndex){
        this.beginIndex = beginIndex;
    }
   
    /**
     * @return
     * Returns the currentPage.
     */

    publicint getCurrentPage(){
        return currentPage;
    }
   
    /**
     * @param currentPage
     * The currentPage to set.
     */

    publicvoid setCurrentPage(int currentPage){
        this.currentPage = currentPage;
    }
   
    /**
     * @return
     * Returns the everyPage.
     */

    publicint getEveryPage(){
        return everyPage;
    }
   
    /**
     * @param everyPage
     * The everyPage to set.
     */

    publicvoid setEveryPage(int everyPage){
        this.everyPage = everyPage;
    }
   
    /**
     * @return
     * Returns the hasNextPage.
     */

    publicboolean getHasNextPage(){
        return hasNextPage;
    }
   
    /**
     * @param hasNextPage
     * The hasNextPage to set.
     */

    publicvoid setHasNextPage(boolean hasNextPage){
        this.hasNextPage = hasNextPage;
    }
   
    /**
     * @return
     * Returns the hasPrePage.
     */

    publicboolean getHasPrePage(){
        return hasPrePage;
    }
   
    /**
     * @param hasPrePage
     * The hasPrePage to set.
     */

    publicvoid setHasPrePage(boolean hasPrePage){
        this.hasPrePage = hasPrePage;
    }
   
    /**
     * @return Returns the totalPage.
     *
     */

    publicint getTotalPage(){
        return totalPage;
    }
   
    /**
     * @param totalPage
     * The totalPage to set.
     */

    publicvoid setTotalPage(int totalPage){
        this.totalPage = totalPage;
    }
   
}



上面的这个Page类对象只是一个完整的Page描述,接下来我写了一个PageUtil,负责对Page对象进行构造:
java代码: 


/*Created on 2005-4-14*/
package org.flyware.util.page;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* @author Joa
*
*/

publicclass PageUtil {
   
    privatestaticfinal Log logger = LogFactory.getLog(PageUtil.class);
   
    /**
     * Use the origin page to create a new page
     * @param page
     * @param totalRecords
     * @return
     */

    publicstatic Page createPage(Page page, int totalRecords){
        return createPage(page.getEveryPage(), page.getCurrentPage(), totalRecords);
    }
   
    /** 
     * the basic page utils not including exception handler
     * @param everyPage
     * @param currentPage
     * @param totalRecords
     * @return page
     */

    publicstatic Page createPage(int everyPage, int currentPage, int totalRecords){
        everyPage = getEveryPage(everyPage);
        currentPage = getCurrentPage(currentPage);
        int beginIndex = getBeginIndex(everyPage, currentPage);
        int totalPage = getTotalPage(everyPage, totalRecords);
        boolean hasNextPage = hasNextPage(currentPage, totalPage);
        boolean hasPrePage = hasPrePage(currentPage);
       
        returnnew Page(hasPrePage, hasNextPage, 
                                everyPage, totalPage,
                                currentPage, beginIndex);
    }
   
    privatestaticint getEveryPage(int everyPage){
        return everyPage == 0 ? 10 : everyPage;
    }
   
    privatestaticint getCurrentPage(int currentPage){
        return currentPage == 0 ? 1 : currentPage;
    }
   
    privatestaticint getBeginIndex(int everyPage, int currentPage){
        return(currentPage - 1) * everyPage;
    }
       
    privatestaticint getTotalPage(int everyPage, int totalRecords){
        int totalPage = 0;
               
        if(totalRecords % everyPage == 0)
            totalPage = totalRecords / everyPage;
        else
            totalPage = totalRecords / everyPage + 1 ;
               
        return totalPage;
    }
   
    privatestaticboolean hasPrePage(int currentPage){
        return currentPage == 1 ? false : true;
    }
   
    privatestaticboolean hasNextPage(int currentPage, int totalPage){
        return currentPage == totalPage || totalPage == 0 ? false : true;
    }
   

}



上面的这两个对象与具体的业务逻辑无关,可以独立和抽象。

面对一个具体的业务逻辑:分页查询出User,每页10个结果。具体做法如下:
1. 编写一个通用的结果存储类Result,这个类包含一个Page对象的信息,和一个结果集List:
java代码: 


/*Created on 2005-6-13*/
package com.adt.bo;

import java.util.List;

import org.flyware.util.page.Page;

/**
* @author Joa
*/

publicclass Result {

    private Page page;

    privateList content;

    /**
     * The default constructor
     */

    public Result(){
        super();
    }

    /**
     * The constructor using fields
     *
     * @param page
     * @param content
     */

    public Result(Page page, List content){
        this.page = page;
        this.content = content;
    }

    /**
     * @return Returns the content.
     */

    publicList getContent(){
        return content;
    }

    /**
     * @return Returns the page.
     */

    public Page getPage(){
        return page;
    }

    /**
     * @param content
     *            The content to set.
     */

    publicvoid setContent(List content){
        this.content = content;
    }

    /**
     * @param page
     *            The page to set.
     */

    publicvoid setPage(Page page){
        this.page = page;
    }
}



2. 编写业务逻辑接口,并实现它(UserManager, UserManagerImpl)
java代码: 


/*Created on 2005-7-15*/
package com.adt.service;

import net.sf.hibernate.HibernateException;

import org.flyware.util.page.Page;

import com.adt.bo.Result;

/**
* @author Joa
*/

publicinterface UserManager {
   
    public Result listUser(Page page)throws HibernateException;

}



java代码: 


/*Created on 2005-7-15*/
package com.adt.service.impl;

import java.util.List;

import net.sf.hibernate.HibernateException;

import org.flyware.util.page.Page;
import org.flyware.util.page.PageUtil;

import com.adt.bo.Result;
import com.adt.dao.UserDAO;
import com.adt.exception.ObjectNotFoundException;
import com.adt.service.UserManager;

/**
* @author Joa
*/

publicclass UserManagerImpl implements UserManager {
   
    private UserDAO userDAO;

    /**
     * @param userDAO The userDAO to set.
     */

    publicvoid setUserDAO(UserDAO userDAO){
        this.userDAO = userDAO;
    }
   
    /* (non-Javadoc)
     * @see com.adt.service.UserManager#listUser(org.flyware.util.page.Page)
     */

    public Result listUser(Page page)throws HibernateException, ObjectNotFoundException {
        int totalRecords = userDAO.getUserCount();
        if(totalRecords == 0)
            throw new ObjectNotFoundException("userNotExist");
        page = PageUtil.createPage(page, totalRecords);
        List users = userDAO.getUserByPage(page);
        returnnew Result(page, users);
    }

}



其中,UserManagerImpl中调用userDAO的方法实现对User的分页查询,接下来编写UserDAO的代码:
3. UserDAO 和 UserDAOImpl:
java代码: 


/*Created on 2005-7-15*/
package com.adt.dao;

import java.util.List;

import org.flyware.util.page.Page;

import net.sf.hibernate.HibernateException;

/**
* @author Joa
*/

publicinterface UserDAO extends BaseDAO {
   
    publicList getUserByName(String name)throws HibernateException;
   
    publicint getUserCount()throws HibernateException;
   
    publicList getUserByPage(Page page)throws HibernateException;

}



java代码: 


/*Created on 2005-7-15*/
package com.adt.dao.impl;

import java.util.List;

import org.flyware.util.page.Page;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;

import com.adt.dao.UserDAO;

/**
* @author Joa
*/

publicclass UserDAOImpl extends BaseDAOHibernateImpl implements UserDAO {

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserByName(java.lang.String)
     */

    publicList 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();
    }

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserCount()
     */

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

    /* (non-Javadoc)
     * @see com.adt.dao.UserDAO#getUserByPage(org.flyware.util.page.Page)
     */

    publicList 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();
    }

}



至此,一个完整的分页程序完成。前台的只需要调用userManager.listUser(page)即可得到一个Page对象和结果集对象的综合体,而传入的参数page对象则可以由前台传入,如果用webwork,甚至可以直接在配置文件中指定。

下面给出一个webwork调用示例:
java代码: 


/*Created on 2005-6-17*/
package com.adt.action.user;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.flyware.util.page.Page;

import com.adt.bo.Result;
import com.adt.service.UserService;
import com.opensymphony.xwork.Action;

/**
* @author Joa
*/

publicclass ListUser implementsAction{

    privatestaticfinal Log logger = LogFactory.getLog(ListUser.class);

    private UserService userService;

    private Page page;

    privateList users;

    /*
     * (non-Javadoc)
     *
     * @see com.opensymphony.xwork.Action#execute()
     */

    publicString execute()throwsException{
        Result result = userService.listUser(page);
        page = result.getPage();
        users = result.getContent();
        return SUCCESS;
    }

    /**
     * @return Returns the page.
     */

    public Page getPage(){
        return page;
    }

    /**
     * @return Returns the users.
     */

    publicList getUsers(){
        return users;
    }

    /**
     * @param page
     *            The page to set.
     */

    publicvoid setPage(Page page){
        this.page = page;
    }

    /**
     * @param users
     *            The users to set.
     */

    publicvoid setUsers(List users){
        this.users = users;
    }

    /**
     * @param userService
     *            The userService to set.
     */

    publicvoid setUserService(UserService userService){
        this.userService = userService;
    }
}



上面的代码似乎看不出什么地方设置了page的相关初值,事实上,可以通过配置文件来进行配置,例如,我想每页显示10条记录,那么只需要:
java代码: 


<?xml version="1.0"?>
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
       
        <package name="user" extends="webwork-interceptors">
               
                <!-- The default interceptor stack name -->
        <default-interceptor-ref name="myDefaultWebStack"/>
               
                <action name="listUser" class="com.adt.action.user.ListUser">
                        <param name="page.everyPage">10</param>
                        <result name="success">/user/user_list.jsp</result>
                </action>
               
        </package>

</xwork>




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

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



这个不错,我刚刚学,不知道page怎么调用? 如果我初始页面的url没有 page.currentpage=1的话,就报错了


我也遇到了与"破碎虚空"同样的问题,调用代码如下
java代码: 


                DetachedCriteria detachedCriteria = DetachedCriteria
                                .forClass(User.class);

                //if (user.getGroup() != null)
                //        detachedCriteria.add(Restrictions.eq("group", user.getGroup()));
               
                PaginationSupport ps = findPageByCriteria(detachedCriteria,startIndex,pageSize);
                return ps;


然后调用 ps.getItems(); 反回的结果里竟然是一个integer类型的数据而不是User对象,
希望Robin贴出使用代码

yangbo9229 写道:
我也遇到了与"破碎虚空"同样的问题,调用代码如下
java代码: 


                DetachedCriteria detachedCriteria = DetachedCriteria
                                .forClass(User.class);

                //if (user.getGroup() != null)
                //        detachedCriteria.add(Restrictions.eq("group", user.getGroup()));
               
                PaginationSupport ps = findPageByCriteria(detachedCriteria,startIndex,pageSize);
                return ps;


然后调用 ps.getItems(); 反回的结果里竟然是一个integer类型的数据而不是User对象,
希望Robin贴出使用代码

java代码: 

int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
criteria.setProjection(null);                                                criteria.setResultTransformer(Criteria.ROOT_ENTITY);


但是如果是有group by的时候 这时的totalcount取的值不对




引用:

DetachedCriteria确实实现的不是很完善,我们希望它是一个无状态的,仅保存查询条件的值对象,但实际上它做不到。一个DetachedCriteria对象反复做分页查询,第一次查询时调用的setMaxResults方法和setFirstResult方法后,这个状态保存在DetachedCriteria上了,会影响下一次count操作,因此每次查询必需new一个DetachedCriteria。同样的原因导致第一个问题交换操作顺序也不行。


在criteria的查询结果List取得过后,在末尾简单的加上
java代码: 

criteria.setFirstResult(0).setMaxResults(totalCount);


还原回初始状态似乎就能简单解决这个问题。


yangbo9229 写道:
我也遇到了与"破碎虚空"同样的问题,调用代码如下
java代码: 


                DetachedCriteria detachedCriteria = DetachedCriteria
                                .forClass(User.class);

                //if (user.getGroup() != null)
                //        detachedCriteria.add(Restrictions.eq("group", user.getGroup()));
               
                PaginationSupport ps = findPageByCriteria(detachedCriteria,startIndex,pageSize);
                return ps;


然后调用 ps.getItems(); 反回的结果里竟然是一个integer类型的数据而不是User对象,
希望Robin贴出使用代码
如何利用DetachedCriteria如何实现数据分页,Robbin已经给出了思路,但是如果Criteria中如果有Order,确实会有问题,楼上几位给出了Criteria和Order分别传递的折中解决办法,但是总感觉不爽,那么,还有别的办法吗?答案是肯定的,看过org.hibernate.impl.CriteriaImpl的代码后,我们发现Order是保存在一个名为orderEntries的List中,可惜的是这个List是Private的,我们没有办法直接操作,但是大家别忘了,还有一种东西叫reflect,下面给出具体的关键代码:
java代码: 

//先去掉Order部分
List orderEntrys=null;
Field field=null;

CriteriaImpl impl = (CriteriaImpl) criteria;
field = CriteriaImpl.class.getDeclaredField("orderEntries");
       
field.setAccessible(true);//这是关键:)
orderEntrys = (List)field.get(impl);
field.set(criteria,newArrayList());

//获取总记录数
int rectotal = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
criteria.setProjection(null);
criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
ps.setRecTotal(rectotal);

//再恢复Order部分
List innerOrderEntries = (List)field.get(criteria);
for(int i=0;i<orderEntrys.size();i++){
  innerOrderEntries.add(orderEntrys.get(i));
}

posted on 2006-06-19 19:40 xiaogang 阅读(4858) 评论(0)  编辑  收藏


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


网站导航: