不知道大家做项目做到最后有什么感觉没有,其实大家做来做去就是做一个列表加上分页和多条件的查询(http://xdwangiflytek.iteye.com/blog/1358261),只是不同的项目业务流程不一样而已,所以今天我想说说这里的分页。
1、 大家需要了解的是为什么我们需要分页?
因为当数据量太大时,会影响查询和传输的性能,并且我们从用户角度来考虑的话,如果让用户一次性看到成千上万条记录那用户也会疯掉的。
2、 对我们来说有哪些可实现的分页技术?
a、 存储过程分页,即在数据库中创建一个存储过程,传入SQL和页码获取当前页的记录,这个需要大家对存储过程有比较好的认识(我这块不行),当然这个从性能上来说是最好的,但是不能跨数据库平台。
b、 使用数据库专有SQL语句进行分页(Oracle的rownum、MSSQL的top、MySql的limit等),性能也很好,但是还是不能跨数据库(其实真实项目中没那么多项目要求都跨数据库)。
c、 JDBC分页,通过Statement的statement.setMaxRow(endIndex)和resultSet.absoulte(beginIndex)取得当前页范围内的记录。此种方式的性能依赖于厂商对JDBC规范的实现。这种方式的通用性是最好的,性能也不错,完全与数据库平台无关了。
d、 根据数据库类型自动生成数据库专有特性的sql语句,其实说白了也就是Hibernate的实现方式,这个自己也写过一个,其实就是根据数据库类型生成不同的数据库SQL专有语句而已。
下面我们需要写一个分页工具类,在写之前我们需要弄明白分页的原理。为了能够取得指定页码所对应的记录,我们是不是需要两个关键的参数:总记录数和每页的记录数;
每页的记录数我们可以设置一个默认值,10、15、20、25都无所谓,根据实际需求。
总记录数就没办法了,需要额外从数据库中利用Count函数取了,通过这两个参数我们是不是可以计算出总页数。同时我们也可以判断用户传过来的页码是否有效(小于第一页OR超出最后一页),然后我们再根据页码和每页记录数是不是就可以计算出当前页的的开始条数和终止条数了。
下面我们就需要来看看分页的工具类了
- package com.iflytek.page;
- /**
- * 分页工具类
- *
- * @author xudongwang 2012-1-19
- *
- * Email:xdwangiflytek@gmail.com
- */
- public class Page {
- /**
- * 总记录数
- */
- private int totalRow;
- /**
- * 每页记录数
- */
- private int pageSize = 10;
- /**
- * 当前页码
- */
- private int currentCount;
- /**
- * 总页数
- */
- private int total;
- /**
- * 起始记录下标
- */
- private int beginIndex;
- /**
- * 截止记录下标
- */
- private int endIndex;
- /**
- * 构造方法,使用总记录数,当前页码
- *
- * @param totalRow
- * 总记录数
- * @param currentCount
- * 当前页面,从1开始
- */
- public Page(int totalRow, int currentCount) {
- this.totalRow = totalRow;
- this.currentCount = currentCount;
- calculate();
- }
- /**
- * 构造方法 ,利用总记录数,当前页面
- *
- * @param totalRow
- * 总记录数
- * @param currentCount
- * 当前页面
- * @param pageSize
- * 默认10条
- */
- public Page(int totalRow, int currentCount, int pageSize) {
- this.totalRow = totalRow;
- this.currentCount = currentCount;
- this.pageSize = pageSize;
- calculate();
- }
- private void calculate() {
- total = totalRow / pageSize + ((totalRow % pageSize) > 0 ? 1 : 0);
- if (currentCount > total) {
- currentCount = total;
- } else if (currentCount < 1) {
- currentCount = 1;
- }
- beginIndex = (currentCount - 1) * pageSize;
- endIndex = beginIndex + pageSize;
- if (endIndex > totalRow) {
- endIndex = totalRow;
- }
- }
- public int getTotalRow() {
- return totalRow;
- }
- public int getPageSize() {
- return pageSize;
- }
- public int getCurrentCount() {
- return currentCount;
- }
- public int getTotal() {
- return total;
- }
- public int getBeginIndex() {
- return beginIndex;
- }
- public int getEndIndex() {
- return endIndex;
- }
- }
继续
在后台获取前台传进来的页码 //从页面取得页码
- int currentPage = 1;
- try {
- currentPage = Integer.parseInt(request.getParameter("currentPage"));
- } catch (Exception ex) {}
- //取得总记录数,创建Page对象
- int totalRow = studentDao.getAllStudents();//通过select count 取得总记录数
- Page page = new Page(totalRow, currentPage);
- studentDao.list(page);
数据访问层,StduentDao.java
- public List<Stduent> getStudentsByPage(Page page) {
- List<Stduent> students = new ArrayList<Stduent>();
- try {
- String sql = "SELECT id,name,email FROM tbl_stduent";
- Connection conn = null;
- try {
- conn = DbUtil.getConnection();
- Statement statement = conn.createStatement();
- statement.setMaxRows(page.getEndIndex());//关键代码,设置最大记录数为当前页记录的截止下标
- ResultSet resultSet = statement.executeQuery(sql);
- if (page.getBeginIndex() > 0) {
- resultSet.absolute(page.getBeginIndex());//关键代码,直接移动游标为当前页起始记录处
- }
- while (resultSet.next()) {
- Stduent student = new Student();
- ……
- students.add(student);
- }
- resultSet.close();
- statement.close();
- } finally {
- if (conn != null) {
- conn.close();
- }
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return students;
- }
其实仔细想想JDBC分页的性能与页码有关即与statement.setMaxRows有效,越往后翻,性能越差,因为越往后一次性查询的记录数就多,但是我们从用户的角度来看不会有用户会牛逼的一页一页翻到第n页去,一般都是根据条件来缩小查询范围。所以折中的办法就是将记录数设大一点,另外就是限制用户翻页的范围,其实这些性能的前提都是在数据量非常大的情况下而言的,一般数据量少的话,基本上都可以忽略不计的。