Struts和Hibernate整合中分页实现解析

1 为什么要分页

我们在做数据库查询的时候,可能查询到的记录较多,显示在一页,显然会使界面看起来有点冗长。依照人的阅读习惯,显示的记录过多,就会不知道看什么,从而对信息失去兴趣,人们普遍能接受的一页的记录数在10左右,所以分页技术在web开发中较为实用,故在此对分页进行总结一下。

在做Struts与Hibernate整合开发的时候,怎样去做分页呢?
一般处理方法分为以下几种:
一、在Struts分页有两种结构:
1.在Action中通过DAO查询出所有的记录,然后加到session或request对象中,传到客户端,由JSP进行分页。

这种方法对于在数据量少的时候很方便,也不影响速度。

2.在Action中每次通过DAO只查询出一页的记录,再传给JSP页面。

这种结构对于数据量大的程序很好,但对于数据量小的情况,会增加对服务器的请求,加大服务器的负载。

二、Hibernate查询

在Hibernate中直接提供了对数据库定点定量的查询方法

如:从第1万条开始取出100条记录

Query q = session.createQuery("from Cat as c");
q.setFirstResult(10);

q.setMaxResults(100);

List l = q.list();

下面对Struts中的第二种方法加以实现

2 Struts中分页实现

原文:http://www.125135.com/935805.htm
http://www.blogjava.net/wujun/archive/2006/08/29/65890.html
新建表

DROP DATABASE IF EXISTS `wjcms`;
CREATE DATABASE `wjcms` /*!40100 DEFAULT CHARACTER SET gb2312 */;
USE `wjcms`;

#
# Table structure for table t_article
#

CREATE TABLE `t_article` (
  `a_id` int(11) NOT NULL auto_increment,
  `a_sort` int(11) NOT NULL default '0',
  `a_title` varchar(50) default NULL,
  `a_body` text,
  `a_author` varchar(11) default '',
  `a_hit` int(11) NOT NULL default '0',
  `c_id` int(11) default '0',
  `a_date` varchar(20) default NULL,
  PRIMARY KEY  (`a_id`)
) 

实体

public class articleVO {
    private int a_id;
    private int a_sort;
    private int a_hit;
    private int c_id;
    private String a_title;
    private String a_body;
    private String a_author;
    private String a_date;
    // getter setter

新建page.java

package page.dal;

public class page {
    private int totalRows; //总行数
    private int pageSize = 10; //每页显示的行数
    private int currentPage; //当前页号
    private int totalPages; //总页数
    private int startRow; //当前页在数据库中的起始行

    public page(int _totalRows) {
     totalRows = _totalRows;
     totalPages=totalRows/pageSize;
     int mod=totalRows%pageSize;
     if(mod>0){
       totalPages++;
     }
     currentPage = 1;
     startRow = 0;
   }

   public int getStartRow() {
     return startRow;
   }

   public int getTotalPages() {
     return totalPages;
   }

   public int getCurrentPage() {
     return currentPage;
   }

   public int getPageSize() {
     return pageSize;
   }

   public void setTotalRows(int totalRows) {
     this.totalRows = totalRows;
   }

   public void setStartRow(int startRow) {
     this.startRow = startRow;
   }

   public void setTotalPages(int totalPages) {
     this.totalPages = totalPages;
   }

   public void setCurrentPage(int currentPage) {
     this.currentPage = currentPage;
   }

   public void setPageSize(int pageSize) {
     this.pageSize = pageSize;
   }

   public int getTotalRows() {
     return totalRows;
   }

   public void first() {
     currentPage = 1;
     startRow = 0;
   }

   public void previous() {
     if (currentPage == 1) {
       return;
     }
     currentPage--;
     startRow = (currentPage - 1) * pageSize;
   }

   public void next() {
     if (currentPage < totalPages) {
       currentPage++;
     }
     startRow = (currentPage - 1) * pageSize;
   }

   public void last() {
     currentPage = totalPages;
     startRow = (currentPage - 1) * pageSize;
   }

   public void refresh(int _currentPage) {
     currentPage = _currentPage;
     if (currentPage > totalPages) {
       last();
     }
   }

 }

Pager类用于计算首页、前一页、下一页、尾页的在数据库中的起始行,当前的页码。
新建 pageHelp.java

package page.dal;
import javax.servlet.http.*;

public class PagerHelp {
    public static page getPager(HttpServletRequest httpServletRequest,int totalRows) {

     //定义pager对象,用于传到页面
     page pager = new page(totalRows);

     //从Request对象中获取当前页号
     String currentPage = httpServletRequest.getParameter("currentPage");

     //如果当前页号为空,表示为首次查询该页
     //如果不为空,则刷新page对象,输入当前页号等信息
     if (currentPage != null) {
       pager.refresh(Integer.parseInt(currentPage));
     }

     //获取当前执行的方法,首页,前一页,后一页,尾页。
     String pagerMethod = httpServletRequest.getParameter("pageMethod");

     if (pagerMethod != null) {
       if (pagerMethod.equals("first")) {
         pager.first();
       } else if (pagerMethod.equals("previous")) {
         pager.previous();
       } else if (pagerMethod.equals("next")) {
         pager.next();
       } else if (pagerMethod.equals("last")) {
         pager.last();
       }
     }
     return pager;
   }

}

//DAO类
新建 util.java

package page.dal;
import net.sf.hibernate.Query;
import net.sf.hibernate.cfg.Configuration;
import java.util.List;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Session;
import java.util.*;
public class util {
    public util() {
    }
     private Session ss=null;
    public Session getSession()
  {
      //  Configuration config=null;
      SessionFactory sessionFactory;
      try {
          Configuration cfg = new Configuration();
          sessionFactory = cfg.addClass(articleVO.class).
                           buildSessionFactory();
          // SessionFactory sessionFactory=config.buildSessionFactory();
          ss = sessionFactory.openSession();
          return ss;
      } catch (HibernateException ex) {
          System.out.print("getsession出错了。。" + ex.getMessage());
          return null;
      }
  }

  public int getCount()
  {
      String sql="select count(*) from articleVO" ;
      this.getSession();

    try {
     // ss.createQuery("select count(a)as cont from articleVO a ");
      int rows= ((Integer) ss.iterate(sql).next()).intValue();
      ss.flush();
      return rows;

    } catch (HibernateException ex) {
        System.out.print("ex::"+ex.getMessage());
        return 0;
    }


  }

  public Collection  getList(int pagesize,int currow) throws HibernateException {
      Collection vehicleList = null;
      this.getSession();
      Query q=ss.createQuery("from articleVO");
      q.setFirstResult(currow);
      q.setMaxResults(pagesize);
      vehicleList=q.list();
      ss.flush();
      return vehicleList;
  }

}

新建 struts PageAction.java

package page.dal;

import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.Action;
import page.dal.*;
import java.util.*;
import net.sf.hibernate.*;

public class pageAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response) {
        Collection clInfos = null;//用于输出到页面的记录集合
        int totalRows;//记录总行数
        util dal=new util();
        totalRows=dal.getCount();
        System.out.print("总行数=="+totalRows);
        page p=PagerHelp.getPager(request,totalRows);
        try {
            clInfos = dal.getList(p.getPageSize(), p.getStartRow());

        } catch (HibernateException ex) {
            System.out.print("action里的错误="+ex.getMessage());
        }
        request.setAttribute("page",p);
        request.setAttribute("list",clInfos);
        return mapping.findForward("page");
        //pageForm pageForm = (pageForm) form;
      //  throw new java.lang.UnsupportedOperationException(
              //  "Method $execute() not yet implemented.");
    }
}

前台页面

<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="/WEB-INF/struts-nested.tld" prefix="nested" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ page contentType="text/html; charset=GBK" %>
<html:html>
<head>
<title>
page
</title>
</head>
<body>
<table align="center" border="2">
<tr>
<th>a_title</th>
<th>a_body</th>
<th>a_a_date</th>
<th>a_author</th>
</tr>

<logic:iterate id="listd" name="list">
<tr>
<td>
<bean:write name="listd" property="a_title"/>
</td>
<td>
<bean:write name="listd" property="a_author"/>
</td>
<td>
<bean:write name="listd" property="a_date"/>
</td>
<td>
<bean:write name="listd" property="a_date"/>
</td>
</tr>
</logic:iterate>

</table><bean:write name="page" property="currentPage"/>页
共<bean:write name="page" property="totalPages" /><html:link action="/pageAction.do?pageMethod=first"
paramName="page" paramProperty="currentPage" paramId="currentPage">首页</html:link>
   <html:link action="/pageAction.do?pageMethod=previous"
paramName="page" paramProperty="currentPage" paramId="currentPage">上一页</html:link>
   <html:link action="/pageAction.do?pageMethod=next"
paramName="page" paramProperty="currentPage" paramId="currentPage">下一页</html:link>

   <html:link action="/pageAction.do?pageMethod=last"
paramName="page" paramProperty="currentPage" paramId="currentPage">尾页</html:link>
</body>
</html:html>

启动浏览 pageAction.do 运行OK。
配置文件

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>

    <class name="page.dal.articleVO" table="t_article" >


    <id name="a_id" column="a_id" unsaved-value="0" >
      <generator class="native"/>
 </id>
     <property name="c_id"    column="c_id"/>
     <property name="a_title" column="a_title"/>
     <property name="a_sort"  column="a_sort"/>
     <property name="a_date"  column="a_date"/>
     <property name="a_body"  column="a_body"/>
     <property name="a_hit"   column="a_hit"/>
     <property name="a_author" column="a_author"/>


  </class>

</hibernate-mapping>

hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class org.gjt.mm.mysql.Driver
hibernate.connection.url jdbc:mysql://localhost:3306/wjcms
hibernate.connection.username root
hibernate.connection.password 
hibernate.connection.pool_size 1
hibernate.proxool.pool_alias pool1
hibernate.show_sql true
hibernate.max_fetch_depth 1
hibernate.cache.use_query_cache true

总结:
其实最关键的是把当前页号和要执行的是功能(上一页,下一页)的参数从页面传进来,在Action中就可以根据这两个参数去取下一个页面上要显示的记录集了

3 解析Hibernate的分页技术

3.1先来解释以下常用分页技术根据什么进行分页

有的数据库在语法上支持分页,而有的数据库则需要使用可滚动游标来实现,并且在不支持可滚动游标的系统上只能使用单向游标逐步接近要取得的数据。
例如:
query.scroll()和query.setFirstResult(),query.setMaxResults();
这两种方法都可以取到一定范围内的数据,用来数据分页显示。那么两者区别,以及两者的效率如何?
1.scroll是用JDBC2.0的可滚动结果集实现;
query.setMaxResults();query.setFirstResult()是数据库SQL语句实现。
2.你说是在数据库就分页好呢?还是把结果集都取到内存再分页好呢?(应该是在数据库就分了好些吧,但是如果在内存分页的话,换页的时候是不是更快一些呢?)
3.在数据库进行分页是首选的方式。数据库分页实际上是利用数据库本身SQL扩展的功能进行分页,例如MySQL的 limit 0,50这样的SQL语句。不但速度快,而且非常节省内存。不过不是每种数据库的都有这种分页支持的SQL,例如SQL Server就不支持。
4.scroll是利用JDBC2.0的功能做分页的,那么就完全取决于特定数据库的JDBC Driver的实现了。事实上大部分JDBC Driver都是把所有的结果集都一次取到内存,然后再分页的。如果这个结果集非常大,例如几万条,不但程序执行速度会很慢,而且很容易导致out of memory。当然个别JDBC Driver使用了服务器端游标来实现,那么就不会导致这种问题,例如jTDS。

由上面的例子,可知,
关于Hibernate的分页技术,无外乎两种:
1.从数据库中取得记录,在内存中再划分。但如果遇到记录数很大的时候效率很成问题。

2.采用Hibernate的物理分页,每次只是取一页。从客户端传进来的是第几页和每页多少条记录,要首先查询符合记录的总记录数,再根据总记录数和当前页,每页记录数可以算出要取的是数据库中的第几条记录。但2次查询不可避免了。

所以总结了两种方式的优劣,如果数据量不是非常大的话(百万以上),采用第一种方法,否则可选择第二种。

3.2 解释以下第二种使用SQL扩展进行分页的原理

数据分页显示,在系统实现中往往会带来较大的工作量,对于基于JDBC的程序而言,不同数据库提供的分页(部分读取)模式往往不同,也带来了数据库间可移植性上的问题。
Hibernate中,通过对不同数据库的统一接口设计,实现了透明化、通用化的分页实现机制。
我们可以通过Criteria.setFirstResult和Criteria.setFetchSize方法设定分页范围,如:

Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("age","20"));
//从检索结果中获取第100条记录开始的20条记录
criteria.setFirstResult(100);
criteria.setFetchSize(20);

同样,Query接口也提供了与其一致的方法。
Hiberante中,抽象类net.sf.hibernate.dialect指定了所有底层数据库的对外统一接口。通过针对不同数据库提供相应的dialect实现,数据库之间的差异性得以消除,从而为上层机制提供了透明的、数据库无关的存储层基础。
对于分页机制而言,dialect中定义了一个方法如下:

public String getLimitString(
    String querySelect,
    boolean hasOffset
)

此方法用于在现有Select语句基础上,根据各数据库自身特性,构造对应的记录返回限定子句。如MySQL中对应的记录限定子句为Limit,而Oracle中,可通过rownum子句实现。
来看MySQLDialect中的getLimitString实现:

public String getLimitString(String sql, boolean hasOffset){
    return new StringBuffer(sql.length()+20)
        .appeng(sql)
        .appeng(hasOffset ?" limit ?,?" : " limit ?")
        .toString();
}

从上面可以看到,MySQLDialect.getLimitString方法的实现实际上是在给定的Select语句后追加MySQL所提供的专有SQL子句limit来实现。
下面是Oracle9Dialect中的getLimitString实现,其中通过Oracle特有的rownum子句实现了数据的部分读取。

public String getLimitString(String sql, boolean hasOffset){
    StringBuffer pagingSelect = new StringBuffer(sql.length()+100);
    if(hasOffset){
        pagingSelect.append("select * from (select row_.*,rownum rownum_ from(");
    }else{
        pagingSelect.append("select * from (");
    }
    paginSelect.appeng(sql);
    if(hasOffset){
        pagingSelect.append(") row_ where rownum<=?)  where rownum_>?");
    }
    else{
        pagingSelect.append(") where rownum<=?");
    }
    return pagingSelect.toString();
}

大多数主流数据库都提供了数据部分读取机制,而对于某些没有提供过相应机制的数据库而言,Hibernate也通过其它途径实现了分页,如通过Scrollable ResultSet,如果JDBC不支持Scrollable ResultSet,Hibernate也会自动通过ResultSet的next方法进行记录定位。
这样,Hibernate通过底层对分页机制的良好封装,使得开发人员无需关心数据分页的细节实现,将数据逻辑和存储逻辑分离开来,在提高生产效率的同时,也大大加强了系统在不同数据库平台之间的可移植性。

3.3 使用SQL扩展进行分页的源码解析

先举个例子:
从第2万条开始取出100条记录

Query q = session.createQuery("from Cat as c");;
q.setFirstResult(20000);;
q.setMaxResults(100);;
List l = q.list();;

那么Hibernate底层如何实现分页的呢?实际上Hibernate的查询定义在net.sf.hibernate.loader.Loader这个类里面,仔细阅读该类代码,就可以把问题彻底搞清楚。

Hibernate2.0.3的Loader源代码第480行以下:

if (useLimit); sql = dialect.getLimitString(sql);;      
PreparedStatement st = session.getBatcher();.prepareQueryStatement(sql, scrollable);

如果相应的数据库定义了限定查询记录的sql语句,那么直接使用特定数据库的sql语句。

然后来看net.sf.hibernate.dialect.MySQLDialect:

public boolean supportsLimit(); {
  return true;
}
public String getLimitString(String sql); {
  StringBuffer pagingSelect = new StringBuffer(100);;
  pagingSelect.append(sql);;
  pagingSelect.append(" limit ?, ?");;
  return pagingSelect.toString();;
}

这是MySQL的专用分页语句,再来看net.sf.hibernate.dialect.Oracle9Dialect:

public boolean supportsLimit(); {
  return true;
}

public String getLimitString(String sql); {
  StringBuffer pagingSelect = new StringBuffer(100);;
  pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");;
  pagingSelect.append(sql);;
  pagingSelect.append(" ); row_ where rownum <= ?); where rownum_ > ?");;
  return pagingSelect.toString();;
}

Oracle采用嵌套3层的查询语句结合rownum来实现分页,这在Oracle上是最快的方式,如果只是一层或者两层的查询语句的rownum不能支持order by。

这里可能有个问题,

Oracle的这种实现如果有order by子句依然有问题。某些时候会导致翻页有记录重复或者遗失,很难找到规律,非常奇怪。 

后来去google了一下,有Oracle专家说需要order by的时候必须带上unique的字段,例如主键或者rowid等。 

另外,在使用这种采用rownum的查询时,尽管速度相对比较快,但是后台Oracle在内存和CPU的消耗上会增加许多。其实除非结果集非常庞大(几万以上),并且必须翻倒很后面(skip的记录很多),采用ResultSet.absolute方法性能还可以,并没有数量级上的差别。 

对问题的解释

这种情况经常出现,包括其他数据库,原因就是排序中出现相同关键字如何处理 
一个是每次分页都重新查询,也就需要重新排序 
另一个是数据库是否执行稳定排序 

首先表有个默认顺序,比如主键递增,对order by: 
如果数据库采取稳定排序,排序关键字相同时,保持默认顺序(主键递增),那么反复查询反复排序得到的结果都是一样的 
如果采用不稳定排序,关键字相同时,顺序不确定,那么反复排序时结果就可能不一样,于是就会出现看似重复、遗漏的情况 

办法很简单,就是排序时加上一个主键,对于各个不稳定排序的数据库系统大都应该这样做 

除此之外,Interbase,PostgreSQL,HSQL也支持分页的sql语句,在相应的Dialect里面,大家自行参考。

如果数据库不支持分页的SQL语句,那么根据在配置文件里面

#hibernate.jdbc.use_scrollable_resultset true 

默认是true,如果你不指定为false,那么Hibernate会使用JDBC2.0的scrollable result来实现分页,看Loader第430行以下:

if ( session.getFactory();.useScrollableResultSets(); ); {
  // we can go straight to the first required row
  rs.absolute(firstRow);;
}
else {
  // we need to step through the rows one row at a time (slow);
  for ( int m=0; m<firstRow; m++ ); rs.next();;
}

如果支持scrollable result,使用ResultSet的absolute方法直接移到查询起点,如果不支持的话,使用循环语句,rs.next一点点的移过去。

可见使用Hibernate,在进行查询分页的操作上,是具有非常大的灵活性,Hibernate会首先尝试用特定数据库的分页sql,如果没用,再尝试Scrollable,如果不行,最后采用rset.next()移动的办法。

在查询分页代码中使用Hibernate的一大好处是,既兼顾了查询分页的性能,同时又保证了代码在不同的数据库之间的可移植性。

3.4 第一种在内存分页的实现

首先看一下我的一个action:

public ActionForward queryZcDoc(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { 
    IZcDocService zcDocService=(IZcDocService)   Application.getInstance().getBean("zcDocServiceProxy");
    List docList=zcDocService.queryZcDoc();
    request.setAttribute("doc", subMessList);
    return mapping.findForward("queryDoc");
}

很简单的代码,就是查询数据,扔到一个List里面,然后setAttribute,再在jsp页面显示就可以了。

接下来谈分页,考虑到了简洁性和通用性,我把分页的代码单独封装到了一个类里面去,下面看看这个类:

public class Fenye {
    public List fenye(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)
    {
    List list=(ArrayList) request.getAttribute("list");
    /*

这里有人可能就看不懂了,为什么要带这些参数?因为我上面的action方法是分页之前的方法,所以不能看出来。

下面贴一下用到分页之后的action方法:

public ActionForward queryZcDoc(ActionMapping mapping, ActionForm form,  HttpServletRequest request, HttpServletResponse response) 
{  
    IZcDocService zcDocService=(IZcDocService)Application.getInstance().  getBean("zcDocServiceProxy");  
    List docList=zcDocService.queryZcDoc();  
    request.setAttribute("list", docList);  
    List subMessList=new Fenye().fenye(mapping, form, request, response);  
    request.setAttribute("doc", subMessList);  
    return mapping.findForward("queryDoc");  
}  

和上面的一比较,其实就多了两行代码,为的就是保持页面的简洁性而使用调用的方法,然后再将需要的数据返回。那接着往下看:

*/  
 List subMessList=null; //这个到时候存的是用分页技术之后的要显示的记录  
int showCount =5; //每页显示的记录数。  
int showPage = 1; //当前显示页码数。  
int size =list.size(); //所取得的数据的总条数。  
int pageCount = (size-1)/showCount + 1; //需要显示的总页数  
if(size<showCount)
{
subMessList = list;
}
String page = request.getParameter("page");
if(page != null)
{
showPage = Integer.parseInt(page); 
}
if((showPage*showCount)<size)
{
subMessList = list.subList((showPage-1)*showCount,showPage*showCount); 
}
else
{
subMessList=list.subList((showPage-1)*showCount,size); 
}
request.setAttribute("showPage",new Integer(showPage)); 
request.setAttribute("pageCount",new Integer(pageCount));
request.setAttribute("size",new Integer(size));
return subMessList;
}
}  

到了这里,java代码就写完了,不多吧加括号一共33行。接下来就要到jsp里面去显示了。也是为了页面的整洁和通用性,我把分页显示的东东放到了一个jsp里面。下面看这个jsp:

%@ page language="java" pageEncoding="gb18030"%>  
<div align=center>  
<br>  
<%  
String method=request.getParameter("method");

method这个参数呢,是要区别对待具体那个action的那个方法

String action=request.getParameter("action"); 

action这个参数的作用,看下面就知道了

int showPage = ((Integer)(request.getAttribute("showPage"))).intValue();  
int size = ((Integer)(request.getAttribute("size"))).intValue();  
int pageCount = ((Integer)(request.getAttribute("pageCount"))).intValue();  
int page1=showPage-1;  
int page2=showPage+1;  
int LastPage=pageCount;  
%>  
<%  
out.println("总共有"+size+"条记录 ");  
 out.println("总共有"+pageCount+"页 ");  
out.println("当前是第"+showPage+"页 ");  
if(showPage > 1)  
{  
out.println("<a href='"+action+".do?method="+method+"&page=1'>第一页</a>");  
}  
else  
{  
out.println("第一页");  
}  
%>  
 <%  
if(showPage > 1)  
{  
out.println("<a href='"+action+".do?method="+method+"&page="+page1+"'>上一页</a>");  
}  
 else  
{   
out.println("上一页");  
 }  
%>  
<%  
if(showPage < pageCount)  
{  
out.println("<a href='"+action+".do?method="+method+"&page="+page2+"'>下一页</a>");  
}  
else  
{  
out.println("下一页");  
}   
%>  
<%  
if(showPage<pageCount)  
{  
out.println("<a href='"+action+".do?method="+method+"&page="+LastPage+"'>尾页</a>");  
}  
 else  
{  
 out.println("尾页");  
 }  
%>  
</div>  

关于这个jsp的代码,不用解释太多了吧。再有就是具体的显示页面中,用<jsp:include page=”../fenye.jsp?action=link”></jsp:include>语句将它包含到相应为止就可以了。

3.5 使用Query.setMaxResults方法分页

Hibernate可以使用Query.setMaxResults方法简单地设置需要查询的最大结果集。
然后Hibernate会自动根据所设置的数据库方言翻译成相应的SQL语句提交给数据库。比如如果数据库是Oracle,SQL Server等,则翻译为类似select … top 10之类的SQL语句,若是MySQL,则翻译为select … limit 10之类的SQL。
举个例子:
uery.setFirstResult(0),query.setMaxResults(4);相当于MySQL中的limit 0, 4;
代码实现:

public void testQuery() {
  Session session = null;
  try {
    session = HibernateUtils.getSession();
    session.beginTransaction();
    Query query = session.createQuery("from User");
    query.setFirstResult(0);//从第一条记录开始
    query.setMaxResults(4);//取出四条记录
    List userList = query.list();
    for (Iterator iter=userList.iterator(); iter.hasNext();) {
      User user = (User)iter.next();
      System.out.println(user.getId());
      System.out.println(user.getName());
    }
    session.getTransaction().commit();
  }catch(Exception e) {
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally {
    HibernateUtils.closeSession(session);
  }
}

自己测试:

HQL: 
      String hql =" from NumberBiz biz where biz.numberDepot.status=?" ; 
     Object[] values = {Constants.NUM_STATUS_NOT_AVAILABLE}; 
     NumberBiz numberBiz = (NumberBiz)this.createQuery(session,hql,values).setFirstResult(0).setMaxResults(1).uniqueResult(); 

console view show_sql: 

    select 
        * 
    from 
        ( select 
            numberbiz0_.id as id83_, 
            numberbiz0_.create_date as create2_83_, 
            numberbiz0_.create_by as create3_83_, 
            numberbiz0_.last_update_date as last4_83_, 
            numberbiz0_.last_update_by as last5_83_, 
            numberbiz0_.deleted as deleted83_, 
            numberbiz0_.addon_services as addon7_83_, 
            numberbiz0_.first_three_digits as first8_83_, 
            numberbiz0_.is_recommended as is9_83_, 
            numberbiz0_.last_four_digits as last10_83_, 
            numberbiz0_.middle_four_digits as middle11_83_, 
            numberbiz0_.minimum_expenses_per_month as minimum12_83_, 
            numberbiz0_.num_type as num13_83_, 
            numberbiz0_.num as num83_, 
            numberbiz0_.number_biz_group_id as number19_83_, 
            numberbiz0_.number_brands as number15_83_, 
            numberbiz0_.number_depot_id as number20_83_, 
            numberbiz0_.number_packages as number16_83_, 
            numberbiz0_.pre_pay as pre17_83_, 
            numberbiz0_.price as price83_ 
        from 
            number_biz numberbiz0_, 
            number_depot numberdepo1_ 
        where 
            numberbiz0_.number_depot_id=numberdepo1_.id 
            and numberdepo1_.status=? 
            ) 
    where 
        rownum <= ? 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值