java中如何实现分页显示

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中如何实现分页显示相关的知识,希望对你有一定的参考价值。

最好有源码 谢谢…………
恩 假分页就好 谢谢

一个简单的JAVA分页方法

定义两个Vector,一个为储存查询所有记录的totalV,另一个储存当前页的记录currentPageV;
总的记录数:int totalSize = totalV.getSize();
每页显示的记录数:int countPerPage;
总页数:int totalPageNum = totalSize/countPerPage;

//如果总的记录数和每页记录数的余数大于零,
//那么总的页数为他们的整除结果加一
if (totalSize%countPerPage > 0 )
totalPageNum = totalSize/countPerPage + 1;

当前的页数:pageNum;

for (int j = 0;j<totalV.size();j++)
//分页,根据当前的页数和每页显示的记录数从totalV中取出记录
//往currentPageV中添加记录;
//如果当前记录在(当前页码-1)*每页显示记录数(包括等于)
//和 当前页码*每页显示记录数(不包括等于)之间的时候;
//就属于该页的数据
if ( (j >= (pageNum - 1) * countPerPage) && (j < pageNum * countPerPage))
currentPageV.addElement(totalV.get(j));

//当currentPageV记录数等于每页显示记录数,
//停止往currentPageV中添加记录
if (currentPageV.size() == countPerPage)
break;


那么,当前页中显示的记录,就是currentPageV中的记录。

第二个分页
在使用数据库的过程中,不可避免的需要使用到分页的功能,可是JDBC的规范对此却没有很好的解决。对于这个需求很多朋友都有自己的解决方案,比如使用Vector等集合类先保存取出的数据再分页。但这种方法的可用性很差,与JDBC本身的接口完全不同,对不同类型的字段的支持也不好。这里提供了一种与JDBC兼容性非常好的方案。
JDBC和分页
Sun的JDBC规范的制定,有时很让人哭笑不得,在JDBC1.0中,对于一个结果集(ResultSet)你甚至只能执行next()操作,而无法让其向后滚动,这就直接导致在只执行一次SQL查询的情况下无法获得结果集的大小。所以,如果你使用的是JDBC1.0的驱动,那么是几乎无法实现分页的。
好在Sun的JDBC2规范中很好的弥补了这一个不足,增加了结果集的前后滚动操作,虽然仍然不能直接支持分页,但我们已经可以在这个基础上写出自己的可支持分页的ResultSet了。

和具体数据库相关的实现方法
有一些数据库,如mysql, Oracle等有自己的分页方法,比如Mysql可以使用limit子句,Oracle可以使用ROWNUM来限制结果集的大小和起始位置。这里以Mysql为例,其典型代码如下:
// 计算总的记录条数
String SQL = "SELECT Count(*) AS total " + this.QueryPart;
rs = db.executeQuery(SQL);
if (rs.next())
Total = rs.getInt(1);
// 设置当前页数和总页数
TPages = (int)Math.ceil((double)this.Total/this.MaxLine);
CPages = (int)Math.floor((double)Offset/this.MaxLine+1);
// 根据条件判断,取出所需记录
if (Total > 0)
SQL = Query + " LIMIT " + Offset + " , " + MaxLine;
rs = db.executeQuery(SQL);

return rs;

毫无疑问,这段代码在数据库是Mysql时将会是漂亮的,但是作为一个通用的类(事实上我后面要提供的就是一个通用类库中的一部分),需要适应不同的数据库,而基于这个类(库)的应用,也可能使用不同的数据库,所以,我们将不使用这种方法。

另一种繁琐的实现方法
我看过一些人的做法(事实上包括我在内,一开始也是使用这种方法的),即不使用任何封装,在需要分页的地方,直接操作ResultSet滚到相应的位置,再读取相应数量的记录。其典型代码如下:
<%
sqlStmt = sqlCon.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,
java.sql.ResultSet.CONCUR_READ_ONLY);
strSQL = "select name,age from test";
//执行SQL语句并获取结果集
sqlRst = sqlStmt.executeQuery(strSQL);
//获取记录总数
sqlRst.last();
intRowCount = sqlRst.getRow();
//记算总页数
intPageCount = (intRowCount+intPageSize-1) / intPageSize;
//调整待显示的页码
if(intPage>intPageCount) intPage = intPageCount;
%>
<table border="1" cellspacing="0" cellpadding="0">
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
<%
if(intPageCount>0)
//将记录指针定位到待显示页的第一条记录上
sqlRst.absolute((intPage-1) * intPageSize + 1);
//显示数据
i = 0;
while(i<intPageSize && !sqlRst.isAfterLast())
%>
<tr>
<td><%=sqlRst.getString(1)%></td>
<td><%=sqlRst.getString(2)%></td>
</tr>
<%
sqlRst.next();
i++;


%>
</table>
很显然,这种方法没有考虑到代码重用的问题,不仅代码数量巨大,而且在代码需要修改的情况下,将会无所适从。

使用Vector进行分页
还见过另一些实现分页的类,是先将所有记录都select出来,然后将ResultSet中的数据都get出来,存入Vector等集合类中,再根据所需分页的大小,页数,定位到相应的位置,读取数据。或者先使用前面提到的两种分页方法,取得所需的页面之后,再存入Vector中。
扔开代码的效率不说,单是从程序结构和使用的方便性上讲,就是很糟糕的。比如,这种做法支持的字段类型有限,int, double, String类型还比较好处理,如果碰到Blob, Text等类型,实现起来就很麻烦了。这是一种更不可取的方案。

一个新的Pageable接口及其实现
很显然,看过上面三种实现方法后,我们对新的分页机制有了一个目标,即:不与具体数据库相关;尽可能做到代码重用;尽可能与原JDBC接口的使用方法保持一致;尽可能高的效率。
首先,我们需要提供一个与java.sql.ResultSet向下兼容的接口,把它命名为Pageable,接口定义如下:
public interface Pageable extends java.sql.ResultSet
/**返回总页数
*/
int getPageCount();
/**返回当前页的记录条数
*/
int getPageRowsCount();
/**返回分页大小
*/
int getPageSize();
/**转到指定页
*/
void gotoPage(int page) ;
/**设置分页大小
*/
void setPageSize(int pageSize);
/**返回总记录行数
*/
int getRowsCount();
/**
* 转到当前页的第一条记录
* @exception java.sql.SQLException 异常说明。
*/
void pageFirst() throws java.sql.SQLException;
/**
* 转到当前页的最后一条记录
* @exception java.sql.SQLException 异常说明。
*/
void pageLast() throws java.sql.SQLException;
/**返回当前页号
*/
int getCurPage();

这是一个对java.sql.ResultSet进行了扩展的接口,主要是增加了对分页的支持,如设置分页大小,跳转到某一页,返回总页数等等。
接着,我们需要实现这个接口,由于这个接口继承自ResultSet,并且它的大部分功能也都和ResultSet原有功能相同,所以这里使用了一个简单的Decorator模式。
PageableResultSet2的类声明和成员声明如下:
public class PageableResultSet2 implements Pageable
protected java.sql.ResultSet rs=null;
protected int rowsCount;
protected int pageSize;
protected int curPage;
protected String command = "";

可以看到,在PageableResultSet2中,包含了一个ResultSet的实例(这个实例只是实现了ResultSet接口,事实上它是由各个数据库厂商分别实现的),并且把所有由ResultSet继承来的方法都直接转发给该实例来处理。
PageableResultSet2中继承自ResultSet的主要方法:
//……
public boolean next() throws SQLException
return rs.next();

//……
public String getString(String columnName) throws SQLException
try
return rs.getString(columnName);

catch (SQLException e) //这里是为了增加一些出错信息的内容便于调试
throw new SQLException (e.toString()+" columnName="
+columnName+" SQL="+this.getCommand());


//……
只有在Pageable接口中新增的方法才需要自己的写方法处理。
/**方法注释可参考Pageable.java
*/
public int getCurPage()
return curPage;

public int getPageCount()
if(rowsCount==0) return 0;
if(pageSize==0) return 1;
//calculate PageCount
double tmpD=(double)rowsCount/pageSize;
int tmpI=(int)tmpD;
if(tmpD>tmpI) tmpI++;
return tmpI;

public int getPageRowsCount()
if(pageSize==0) return rowsCount;
if(getRowsCount()==0) return 0;
if(curPage!=getPageCount()) return pageSize;
return rowsCount-(getPageCount()-1)*pageSize;

public int getPageSize()
return pageSize;

public int getRowsCount()
return rowsCount;

public void gotoPage(int page)
if (rs == null)
return;
if (page < 1)
page = 1;
if (page > getPageCount())
page = getPageCount();
int row = (page - 1) * pageSize + 1;
try
rs.absolute(row);
curPage = page;

catch (java.sql.SQLException e)


public void pageFirst() throws java.sql.SQLException
int row=(curPage-1)*pageSize+1;
rs.absolute(row);

public void pageLast() throws java.sql.SQLException
int row=(curPage-1)*pageSize+getPageRowsCount();
rs.absolute(row);

public void setPageSize(int pageSize)
if(pageSize>=0)
this.pageSize=pageSize;
curPage=1;


//PageableResultSet2的构造方法:
public PageableResultSet2(java.sql.ResultSet rs) throws java.sql.SQLException
if(rs==null) throw new SQLException("given ResultSet is NULL","user");

rs.last();
rowsCount=rs.getRow();
rs.beforeFirst();
this.rs=rs;


/*如果要提高效率,可以利用select count(*) 语句取得所有记录数,注释掉
构造函数的rs.last();rowsCount=rs.getRow();rs.beforeFirst();三句。
在调用构造函数后调用此方法获得所有的记录,参数是select count(*)后的结果集
*/
public void setRowsCount(java.sql.ResultSet rs)throws java.sql.SQLException
if(rs==null) throw new SQLException("given ResultSet is NULL","user");
rowCount=rs.getInt(1);


这里只是简单的取得一个总记录数,并将记录游标移回初始位置(before first),同时将参数中的ResultSet赋给成员变量。

Pageable的使用方法
因为Pageable接口继承自ResultSet,所以在使用方法上与ResultSet一致,尤其是在不需要分页功能的时候,可以直接当成ResultSet使用。而在需要分页时,只需要简单的setPageSize, gotoPage,即可。
PreparedStatement pstmt=null;
Pageable rs=null;
……//构造SQL,并准备一个pstmt.
rs=new PageableResultSet2(pstmt.executeQuery());//构造一个Pageable
rs.setPageSize(20);//每页20个记录
rs.gotoPage(2);//跳转到第2页
for(int i=0; i<rs.getPageRowsCount(); i++)//循环处理
int id=rs.getInt(“ID”);
……//继续处理

rs.next();


总结
一个好的基础类应该是便于使用,并且具备足够的可移植性,同时要保证其功能的完善。在上面的实现中,我们从java.sql.ResultSet接口继承出Pageable,并实现了它。这就保证了在使用中与JDBC原有操作的一致性,同时对原有功能没有缩减。
同时它也是易于使用的,因为封装了一切必要的操作,所以在你的代码中唯一显得"难看"和"不舒服"的地方就是需要自己去构造一个PageableResultSet2。不过只要你愿意,这也是可以解决的。
当然它也有具有充分的可移植性,当你将数据库由Oracle变为Mysql或者SQLServer的时候,你仍然可以使用这些分页的代码。它在使用中(或者说在移植的过程中)唯一的限制就是你必须要使用一个支持JDBC2的驱动(现在明白为什么我把类命名为PageableResultSet2了吧。:P),不过,好在JDBC2已经成为标准了,绝大多数的数据库(如Oracle, Mysql, SQLServer)都有自己的或者第三方提供的JDBC2的驱动。
OK,这个分页的实现是否对你的编程有帮助呢?仔细看看,其实真正自己写的代码并不多的,大部分都只是简单的转发操作。一个合适的模式应用可以帮你很大忙。

这里只是简单的取得一个总记录数,并将记录游标移回初始位置(before first),同时将参数中的ResultSet赋给成员变量。
参考技术A

  java分页显示数据有swing桌面应用与web应用两种,其中以web应用居多,而分布又有两种方法,一是在本地分布,二是在数据库分布。区别:

     本地:将需要显示的数据全部取到,再用JS进行控制一部分一部分的显示,不适合于大量数据;

    数据库:利用sql语句,分页取得数据,再在界面显示。(推荐);

  别外,网上也有很多相关人士做的分页工具类,可以查阅参考,对于分页技术,目前主流使用ajax无刷新技术进行分页,对数据的快速响应,用户的体验的都非常不错,值得推荐,符上jsp的分页示例以供参考。

 

<a href="findAllUser?pageNo="1">第一页</a>
findAllUser后面的问号表示要传递参数,pageNo是参数的名字,1是参数的值,通过这种方式传递值与通过表单提交信息的方式的效果完全相同,相当于有一个表单元素,表单元素的名字是pageNo,表单元素的值是1。
要添加到上一页的超链接,可以使用下面的代码:
      <a href="findAllUser?pageNo=$pageNo-1">上一页</a>
要添加到下一页的超链接,可以使用下面的代码:
      <a href="findAllUser?pageNo=$pageNo+1">下一页</a>
要添加到最后一页的超链接,可以使用下面的代码:
      <a href="findAllUser?pageNo=$pageCount">最后一页</a>
如果当前页是第一页,则不用显示“第一页”和“首页”超链,如果是最后一页,则不用显示“尾页”和“下一页”超链。对这两种情况需要控制,进行控制的代码如下:
   <!--如果是第一页,则不显示超链接-->
   <c:if test="$pageNo==1">
      第一页
      上一页
   </c:if>
   <!--如果不是第一页,则显示超链接-->
   <c:if test="$pageNo!=1">
      <a href="findAllUser?pageNo=1">第一页</a>
      <a href="findAllUser?pageNo=$pageNo-1">上一页</a>
   </c:if>
   <!--如果是最后一页,则不显示超链接-->
   <c:if test="$pageNo==pageCount">
      下一页
      最后一页
   </c:if>
   <!--如果不是第一页,则显示超链接-->
   <c:if test="$pageNo!=pageCount">
      <a href="findAllUser?pageNo=$pageNo+1">下一页</a>
      <a href="findAllUser?pageNo=$pageCount">最后一页</a>
   </c:if>
 
   <form action="findAllUser">
     跳转到<input type="text" name="pageNo">页<input type="submit" value="跳转">
   </form>
在显示信息的时候,需要进行控制,只要为<c:forEach>添加begin和end属性即可,控制的代码如下:
   <c:forEach items="$userlist" var="user" begin="$(pageNo-1)*10"
       end="$pageNo*10">

  相关参数资料:

  http://blog.csdn.net/hjjzhangkui/article/details/5639783;

  http://blog.csdn.net/javaeeteacher/article/details/1596573;


参考技术B 给你一个Oracle下的通有方法jdbc
/**
*
* @param <T>
* @param tableName 表名
* @param pageSize 每页大小
* @param currentPage 当前页
* @param condition 条件
* @param order 排序
* @param clazz 返回的POJO对象类型(把查询结果自动封装成与表结构对应的对象,模拟hibernate内部实现过程)
* @return 带一系列参数的结果集,包括当前页,总记录数,每页大小,数据集合
* @throws Exception
*/
public static <T> PageBean<T> getResultListBySQL2(String tableName,
int pageSize, int currentPage, String condition, String order,
Class<T> clazz) throws Exception
PageBean<T> bean = new PageBean<T>();
Connection conn = DBPool.getConnection();
// long start = System.currentTimeMillis();
int allRecords = 0;
int allPages = 0;
String sql = "select count(*) from " + tableName;
if (!(null == condition || "".equals(condition)))
sql = sql + " WHERE " + condition;
// System.out.println(sql);

PreparedStatement psmt = null;

psmt = conn.prepareStatement(sql);
ResultSet rs = psmt.executeQuery();
rs.next();
allRecords = rs.getInt(1);
if (allRecords % pageSize == 0)
allPages = allRecords / pageSize;
else
allPages = (allRecords / pageSize) + 1;

int st = pageSize * (currentPage - 1) + 1;
int end = pageSize * currentPage;

// sql= "SELECT * FROM ";
sql = "SELECT t1.* FROM " + tableName + " t1 ";
if (!(null == condition || "".equals(condition)))
// 如果条件为空
sql += " WHERE " + condition;

if (!(null == order || "".equals(order)))
sql += " ORDER BY " + order;


sql = "SELECT t3.* FROM (SELECT rownum rn,t2.* FROM (" + sql
+ ") t2 ) t3 WHERE t3.rn>=? AND t3.rn<=?";

// sql = "SELECT t.* FROM (SELECT ROWNUM rn,e.* FROM " + tableName
// + " e ) t WHERE t.rn<=" + end + " AND t.rn>=" + st + " ";
// if (!(null == condition || "".equals(condition)))
// sql = "SELECT t.* FROM (SELECT t1.* FROM "
// + tableName + " t1 WHERE " + condition + ") t WHERE t.rn<="
// + end + " AND t.rn>=" + st + " ";
//
// System.out.println(sql);
psmt = conn.prepareStatement(sql);
psmt.setInt(1, (currentPage - 1) * pageSize + 1);
psmt.setInt(2, pageSize * currentPage);
rs = psmt.executeQuery();

// System.out.println("总记录数是:" + allRecords + " ,总页数是" + allPages
// +",dangqianye,"+currentPage);
List<T> list = getListQuery(rs, clazz);
// System.out.println("SQL花时间:" + (System.currentTimeMillis() - start)+
// " ms.")
bean = new PageBean<T>();
bean.setData(list);
bean.setCurPage(currentPage);
bean.setMaxPage(allPages);
bean.setMaxRow(allRecords);
bean.setRowsPerPage(pageSize);
rs.close();
rs = null;
psmt.close();
psmt = null;
DBPool.releaseConn(conn);
return bean;
参考技术C 有真分页还有假分页的说法,假分页数据库读取不分页,显示分页,真分页,数据库读取的时候只读取所要条数 参考技术D 下面给出Struts + Hibernate结合写出分页的DEMO:
/**
* 根据最大页数、开始记录数返回对应记录集
* @param pageSize 最大页数
* @param page 开始记录数
* @return
* @throws Exception
*/
public List getRSofPage(int pageSize,int page) throws Exception
List retList = new ArrayList();
Session sess = null;
try
sess = HibernateUtil.currentSession();
Transaction tx = sess.beginTransaction();
Query q = sess
.createQuery( "from Channel where ParentId is not 0 order by ParentId ,ChannelId ");
q.setMaxResults( pageSize );
q.setFirstResult( (page - 1) * pageSize );
retList = q.list();
tx.commit();
//log
logger.info( "(@@@@@@@@@@@ 根据最大页数、开始记录数返回对应记录集执行正常 @@@@@@@@@@@) ");
catch (HibernateException he)
//log
logger.error( "(@@@@@@@@@@@ 根据最大页数、开始记录数返回对应记录集执行异常 @@@@@@@@@@@) ", he);
new org.hibernate.HibernateException( "getRSofPage(): "+listErrors);
finally
try
HibernateUtil.closeSession();
catch (HibernateException he)
new org.hibernate.HibernateException(
"HibernateUtilServlet.closeSession() ");


return retList;

JAVA-分页查询

分页查询

分页查询将数据库中庞大的数据分段显示,每页显示用户自定义的行数,提高用户体验度,最主要的是如果一次性从服务器磁盘中读出全部数据到内存,有内存溢出的风险

真假分页

假分页: 其原理还是将所有的数据读到内存中,翻页从内存中读取数据,
优点: 实现简单,性能高
缺点:如果数据大容易造成内存溢出

真分页: 每次翻页从数据库查询数据(即磁盘)
优点 : 不容易造成内存溢出
缺点: 实现复杂,性能相对低一些

分页效果

一般分页的功能包括: 首页 上一页 下一页 末页 当前是多少页 总共多少页 一共多少行数据 跳转到第几页 每页多少条数据 我们需要将这个数据查询出来封装到一个对象中,体现了封装思想,也节省了很多复杂代码

分页的设计

从上面请求访问的分页演示的效果图分析分页是如何设计?

分页需要传递的参数

需要用户传入的参数:

  • currentPage: 当前页,跳转到第几页,如第一次访问默认值是1
  • pageSize: 每页显示多少行数据, 如第一次访问默认值 10条

分页需要展示的数据

从分页效果图中可以看出,分页需要依赖的数据:

  1. 当前页的货品信息(也就是商品的集合数据)
  2. 分页条信息:
    • beginPage: 首页
    • prevPage: 上一页
    • nextPage: 下一页
    • totalPage: 总页数/末页
    • totalCount/rows: 总条数
    • currentPage: 当前页
    • pageSize: 每页显示多少条数据

分页需要展示的数据的来源

来源于用户上传: 当前页 , 每页显示多少条数据
来源于数据库查询 : 数据总条数 , 每一页需要展示的商品集合信息
来源于根据上面的已知信息在代码中计算 : 总页数 , 上一页 , 下一页

查询结果总数与结果集的SQL

第一条sql 查询数据库中有多少条数据,COUNT后面不能有空格

SELECT COUNT(*) FROM 表名

第二条sql 根据传入的参数查询第几页,一页多少条数据的结果集

# 第一个 ?:从哪一个索引的数据开始查询(默认从 0 开始)
# 第二个 ?:查询多少条数据
SELECT * FROM 表名 LIMIT ?, ?

接下来分析第二条 SQL 中两个 ? 取值来源:
假设 product 表中有 21 条数据,每页分 5 条数据:
查询第一页数据:SELECT * FROM product LIMIT 0, 5
查询第二页数据:SELECT * FROM product LIMIT 5, 5
查询第三页数据:SELECT * FROM product LIMIT 10, 5
查询第四页数据:SELECT * FROM product LIMIT 15, 5
通过寻找规律发现:第一个 ? 取值来源于 (currentPage - 1) * pageSize;第二个 ? 取值来源于
pageSize,即都来源于用户传递的分页参数。

总页数,上一页和下一页

总页数、上一页和下一页都是来源于程序计算出来的

// 优先计算总页数
int totalPage = rows % pageSize == 0 ? rows / pageSize : rows / pageSize + 1;
//上一页等于当前页-1,但不能超过1页界限
int prevPage = currentPage - 1 >= 1 ? currentPage - 1 : 1;
//下一页等于当前页+1,但不能超过总页数界限
int nextPage = currentPage + 1 <= totalPage ? currentPage + 1 : totalPage;

分页查询实现

访问流程:

封装需要展示的数据成一个对象

如果不封装数据,每个数据都需要存到作用域中,数据太分散,不方便统一管理

/**
* 封装结果数据(某一页的数据)
*/
@Getter
public class PageResult<T> 
	// 两个用户的输入
	private int currentPage; // 当前页码
	private int pageSize; // 每页显示的条数
	// 两条 SQL 语句执行的结果
	private int totalCount; // 总条数
	private List<T> data; // 当前页结果集数据
	// 三个程序计算的数据
	private int prevPage; // 上一页页码
	private int nextPage; // 下一页页码
	private int totalPage; // 总页数/末页页码
	// 分页数据通过下面构造器封装好
	public PageResult(int currentPage, int pageSize, int totalCount, List<T>
	data) 
		this.currentPage = currentPage;
		this.pageSize = pageSize;
		this.totalCount = totalCount;
		this.data = data;
		// 计算三个数据
		this.totalPage = totalCount % pageSize == 0 ? totalCount / pageSize :
		totalCount / pageSize + 1;
		this.prevPage = currentPage - 1 >= 1 ? currentPage - 1 : 1;
		this.nextPage = currentPage + 1 <= this.totalPage ? currentPage + 1 :
		this.totalPage;
	

持久层DAO分页功能实现

分页其最终的所有功能需要依赖的 SQL 就两条:

  • 查询数据的总数(为了显示分页条信息)。
  • 查询当前页的数据。
    所以我们就得给 DAO 增加两个方法,一个查询数据总量,一个查询当前页的商品数据集合。

Mybatis提供的操作方法只能传入一个参数执行SQL任务,而我们现在查询某一页的数据,需要知道是第几页和每页多少条数据这两个参数,所以我们需要将这两个参数封装在一个对象里面(或者使用Map)
编写一个类(起名叫查询对象类)来封装这些查询数据

@Setter
@Getter
/**
* 封装分页查询需要的两个请求传入的分页参数
*/
public class QueryObject 
	private int currentPage = 1; // 当前页码,要跳转到哪一页的页码(需要给默认值)
	private int pageSize = 3; // 每页显示条数(需要给默认值)

再书写持久层DAO接口和实现类

//DAO接口提供两个根据查询对象的查询方法
int queryForCount();
List<Product> queryForList(QueryObject qo);
//DAO实现类
@Override
//查询数据库总数据条数
public int queryForCount() 
	SqlSession session = MyBatisUtil.getSession();
	int totalCount =
	session.selectOne("cn.xxx.mapper.ProductMapper.queryForCount");
	session.close();
	return totalCount;

@Override
//查询某一页的结果集
public List<Product> queryForList(QueryObject qo) 
	SqlSession session = MyBatisUtil.getSession();
	List<Product> products =
	session.selectList("cn.xxx.mapper.ProductMapper.queryForList",qo);
	session.close();
	return products;

修改productMapper.xml

<select id="queryForCount" resultType="int">
SELECT COUNT(*) FROM product
</select>
<select id="queryForList" resultType="cn.xxx.domain.Product">
SELECT * FROM product LIMIT #start, #pageSize
</select>

修改QueryObject.java

给这个类增加getStart方法,返回根据当前页,每页大小这两个数据计算出来这一页的数据在数据库表中的起始索引

@Setter
@Getter
/**
* 封装分页查询需要的两个请求传入的分页参数
*/
public class QueryObject 
	private int currentPage = 1; // 当前页码,要跳转到哪一页的页码(需要给默认值)
	private int pageSize = 3; // 每页显示条数(需要给默认值)
	// 用于 Limit 子句第一个 ? 取值
	public int getStart()
		return (currentPage - 1) * pageSize;
	

业务层ProductService

调用持久层DAO完成数据的查询,并将多个数据封装到一个对象中

//IProductService接口
public interface IProductService 
	/**
	* 完成查询某一页的业务逻辑功能
	*/
	PageResult<Product> query(QueryObject qo);

//Service实现类
public class ProductServiceImpl implements IProductService 
	private IProductDAO productDAO = new ProductDAOImpl();
	@Override
	public PageResult<Product> query(QueryObject qo) 
		// 调用 DAO 查询数据数量
		int totalCount = productDAO.queryForCount();
		// 为了性能加入判断,若查询的数据数量为 0,说明没有数据,返回返回空集合,即集合中没有元素,代表没查询到数据
		if(totalCount == 0)
			return new PageResult(qo.getCurrentPage(), qo.getPageSize(),
			totalCount, Collections.emptyList());
		
		// 执行到这里代表有数据,查询当前页的结果数据
		List<Product> products = productDAO.queryForList(qo);
		return new PageResult(qo.getCurrentPage(), qo.getPageSize(), totalCount,
		products);
	

前台分页功能实现

包含编写 Servlet 及 JSP,Servlet 处理请求,调用业务方法,把查询到数据共享到 JSP 中,展示给用户看。

  1. 必须先完成业务层组件,保证后台测试通过。
  2. 遵循 MVC 思想。
  3. 浏览器发出分页请求参数(去往第几页/每页多少条数据),在 Servlet 中接收这些参数,并封装
  4. 到 QueryObject 对象,调用 Service 中分页查询方法(query)。
  5. 把得到的分页查询结果对象(PageResult)共享在请求作用域中,跳转到 JSP,显示即可。
  6. 修改 JSP 页面,编写出分页条信息(分页条中的信息来源于 PageResult 对象)。

修改ProductServlet.java和展示jsp

  1. 获取页面请求参数,判断是查询操作,调用查询方法,获取分页参数
  2. 将参数封装成查询对象QueryObject
  3. 调用业务层方法查询某一页数据
  4. 将查询出来的结果存到作用域中
  5. 转发到展示页面jsp
  6. 在jsp从作用域中将结果取出来响应到浏览器
//创建业务层对象
private IProductService productService = new ProductServiceImpl();

protected void list(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException 
	QueryObject qo = new QueryObject();
	// 获取请求参数 currentPage,并转型封装
	String currentPage = req.getParameter("currentPage");
	if(StringUtil.hasLength(currentPage)) 
		qo.setCurrentPage(Integer.valueOf(currentPage));
	
	// 获取请求参数 pageSize,并转型封装
	String pageSize = req.getParameter("pageSize");
	if(StringUtil.hasLength(pageSize)) 
		qo.setPageSize(Integer.valueOf(pageSize));
	
	// 调用业务层方法来处理请求查询某一页数据
	PageResult<Product> pageResult = productService.query(qo);
	// 把数据共享给 list.jsp
	req.setAttribute("pageResult", pageResult);
	// 控制跳转到 list.jsp 页面
	req.getRequestDispatcher("/WEB-INF/views/product/list.jsp").forward(req,
	resp);

修改jsp文件,使用JSTL+EL获取作用域中的数据

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
	<head>
		<title>产品列表</title>
		<script type="text/javascript">
			window.onload = function () 
				var trClzs = document.getElementsByClassName("trClassName");
				for(var i = 0; i < trClzs.length; i++)
					trClzs[i].onmouseover = function () 
						console.log(1);
						this.style.backgroundColor = "gray";
					
					trClzs[i].onmouseout = function () 
						console.log(2);
						this.style.backgroundColor = "";
					
				
			
			// 分页 JS
			function changePageSize() 
				document.forms[0].submit();
			
		</script>
	</head>
	<body>
		<a href="/replaceImg.jsp"><img src="$USER_IN_SESSION.headImg" title="更换头
		像"/></a><br/>
		<a href="/product?cmd=input">添加</a>
		<form action="/product">
			<table border="1" cellspacing="0" cellpadding="0" width="80%">
				<tr>
					<th>编号</th>
					<th>货品名</th>
					<th>分类编号</th>
					<th>零售价</th>
					<th>供应商</th>
					<th>品牌</th>
					<th>折扣</th>
					<th>进货价</th>
					<th>操作</th>
				</tr>
				<c:forEach var="product" items="$pageResult.data" varStatus="status">
					<tr class="trClassName">
						<td>$status.count</td>
						<td>$product.productName</td>
						<td>$product.dir_id</td>
						<td>$product.salePrice</td>
						<td>$product.supplier</td>
						<td>$product.brand</td>
						<td>$product.cutoff</td>
						<td>$product.costPrice</td>
						<td>
							<a href="/product?cmd=delete&id=$product.id">删除</a>
							<a href="/product?cmd=input&id=$product.id">修改</a>
						</td>
					</tr>
				</c:forEach>
				<tr align="center">
					<td colspan="9">
						<a href="/product?currentPage=1">首页</a>
						<a href="/product?currentPage=$pageResult.prevPage">上一页</a>
						<a href="/product?currentPage=$pageResult.nextPage">下一页</a>
						<a href="/product?currentPage=$pageResult.totalPage">尾页</a>
						当前第 $pageResult.currentPage / $pageResult.totalPage 页
						一共 $pageResult.totalCount 条数据
						跳转到<input type="number" onchange="changePageSize()"
						name="currentPage" value="$pageResult.currentPage" style="width: 60px;"
						每页显示
						<select name="pageSize" onchange="changePageSize()">
							<option value="3" $pageResult.pageSize == 3 ? 'selected' : ''> 3 </option>
							<option value="5" $pageResult.pageSize == 5 ? 'selected' : ''> 5 </option>
							<option value="8" $pageResult.pageSize == 8 ? 'selected' : ''> 8 </option>
						</select>条数据
					</td>
				</tr>
			</table>
		</form>
	</body>
</html>

常见问题

若刚开始翻页操作成功,但是翻几页之后就不能翻页了,只能重启 Tomcat 才可以翻页,但
是操作几次又不能翻页了。问题原因:在 DAO 中没有关闭 SqlSession 对象释放资源。

以上是关于java中如何实现分页显示的主要内容,如果未能解决你的问题,请参考以下文章

Java中如何实现分页功能

SQLSERVER如何实现分页查询?

在JSP中如何实现分页技术啊?

java jsp分页页码问题?

java ajax分页问题

jsp 如何将查询结果实现分页,最好简单易懂