Java数据分页
来源:互联网 发布:php黑客技术网站源码 编辑:程序博客网 时间:2024/05/22 08:07
Java数据分页的设计及实现
概述
数据分页,对于一个Web程序而言,是不可或缺的一个基础功能。当数据量很小很小的时候,比如只有只有二三十笔,不提供数据分页功能或许还是可以接受的;当数据量达到五十笔、八十笔的时候,如果还不提供分页功能,会显得有些差强人意了;当数据量达到上百、上千甚至上万笔的时候,如果再不提供分页功能,我想没有哪个用户是能够接受得了的了。
解决方案
数据分页,主要有两种解决方案:一是在数据库端进行分页查询;二是一次性将数据全部抓取到客户端,由客户端进行分页处理。这两种方案各有利弊,这里就不多赘述。通常使用第一种解决方案比较多,我这里也选择第一种方案,并以Mysql数据库为例,为大家讲解我的设计。
在数据库端进行分页查询,只需要使用Mysql数据库中自带的limit关键字即可实现,我需要的做的只是需要计算出数据偏移量,以及每次获取记录的笔数。
数据偏移量 = (页码 - 1) * 每页数据笔数
举例说明,假设我们每页显示20笔记录,第1页的偏移量就是(1-1)*20=0,即从第1笔记录开始,连续读取20笔记录;第2页偏移量就是(2-1)*20=20,即从第21笔记录开始,连续读取20笔记录...以此类推。
数据库的查询搞定了,下面就开始思考Java代码的设计。本文的代码设计是在上一篇博文《Java Spring MVC分层设计》的基础进行构建的。
代码交互时序图
代码设计
最初的构想是这样的,定义一个接口IPagination,用来保存存分页信息和数据。
- package com.emerson.etao.utils;
- import java.util.List;
- /**
- * 数据分页接口
- *
- * @author Chris Mao(Zibing)
- *
- */
- public interface IPagination<T> {
- /**
- * 每页显示的数据记录数
- */
- public static final int PAGE_SIZE = 20;
- /**
- * 设置当前页面索引值
- *
- * @param pageIndex
- */
- public void setCurrentPage(int pageIndex);
- /**
- * 设置总行数,并计算出分页数
- *
- * @param totalRows
- */
- public void setTotalRows(int totalRows);
- /**
- * 当前页面索索值
- *
- * @return int
- */
- public int getCurrentPage();
- /**
- * 总行数
- *
- * @return int
- */
- public int getTotalRows();
- /**
- * 总页面数
- *
- * @return int
- */
- public int getTotalPages();
- /**
- * 当前页面的数据记录集合
- *
- * @return List<T>
- */
- public List<T> getData();
- }
然后在DAO层代码实现该接口。
- public abstract class BaseDao<T> implements IBaseDao<T>, IPagination<T>
但是实践下来发现,这样会破坏DAO层代码的原子性,所以打算使用单独的类来实现该接口。但是又考虑到这个类,不可能单独使用,它需要使用到DAO类中的代码与数据库交互,比如查询记录总行数,于是这里使用了Java内部类特性,在DAO代码内声明一个内部类并实现接口IPagination。
下面是DAO基类代码。
- package com.emerson.etao.dao;
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.util.List;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.emerson.etao.db.DBUtils;
- import com.emerson.etao.utils.IPagination;
- /**
- *
- * 数据访问层基类
- *
- * 所有数据访问对象都需要继承此类
- *
- * @author Chris Mao(Zibing)
- *
- */
- public abstract class BaseDao<T> implements IBaseDao<T> {
- protected static final Logger logger = LoggerFactory.getLogger(BaseDao.class);
- /**
- * 获取数据库连接
- *
- * @return java.sql.Connection
- */
- public Connection getConnection() {
- return DBUtils.getConnection();
- }
- /**
- * 关闭数据库连接
- *
- * @param conn
- * @see java.sql.Connection
- */
- public void closeConnection(Connection conn) {
- DBUtils.closeConnection(conn);
- }
- /**
- * 关闭Statement对象,会将对应的数据库连接一并关闭
- *
- * @param stmt
- * @see java.sql.Statement
- */
- @Override
- public void closeStatement(Statement stmt) {
- DBUtils.closeStatement(stmt);
- }
- /**
- * 创建Statement对象,如果参数readOnly为true,则创建一个只读的Statement对象
- *
- * @param conn
- * @param readOnly
- * @return
- * @see java.sql.Connection
- * @see java.sql.Statement
- */
- @Override
- public Statement createStatement(Connection conn, boolean readOnly) {
- return DBUtils.createStatement(conn, readOnly);
- }
- @Override
- public int getTotalRowCount(String sqlStr) throws SQLException {
- int result = 0;
- Connection conn = this.getConnection();
- Statement stmt = this.createStatement(conn, true);
- try {
- ResultSet rs = stmt.executeQuery(String.format("SELECT COUNT(*) AS F1 FROM (%s) AS T1", sqlStr));
- while (rs.next()) {
- result = rs.getInt(1);
- }
- rs.close();
- return result;
- } finally {
- this.closeStatement(stmt);
- }
- }
- /**
- * 分页查询对象
- *
- * @author Chris Mao(Zibing)
- *
- */
- protected class Pagination implements IPagination<T> {
- /**
- * 当前页号
- */
- private int currentPage;
- /**
- * 总记录数
- */
- private int totalRows;
- /**
- * 总页数
- */
- private int totalPages;
- /**
- * 查询偏移量
- */
- private int offset;
- /**
- * 查询数据的SQL语句
- */
- private String sqlStatement = null;
- public Pagination(String sqlStatement) {
- this.sqlStatement = sqlStatement;
- }
- @Override
- public int getCurrentPage() {
- return currentPage;
- }
- @Override
- public void setCurrentPage(int pageIndex) {
- if (pageIndex < 1) {
- currentPage = 1;
- } else if (pageIndex > totalPages) {
- currentPage = totalPages;
- } else {
- currentPage = pageIndex;
- }
- offset = (currentPage - 1) * IPagination.PAGE_SIZE;
- }
- @Override
- public int getTotalRows() {
- return this.totalRows;
- }
- @Override
- public void setTotalRows(int totalRows) {
- this.totalRows = totalRows;
- if (totalRows % IPagination.PAGE_SIZE == 0) {
- totalPages = totalRows / IPagination.PAGE_SIZE;
- } else {
- totalPages = totalRows / IPagination.PAGE_SIZE + 1;
- }
- }
- @Override
- public int getTotalPages() {
- return totalPages;
- }
- @Override
- public List<T> getData() {
- List<T> result = null;
- try {
- result = getAll(getMysqlPageSQL());
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return result;
- }
- /**
- *
- * @return
- */
- public String getMysqlPageSQL() {
- return sqlStatement.concat(String.format(" limit %d, %d ", offset, IPagination.PAGE_SIZE));
- }
- }
- public IPagination<T> getPagination(int pageIndex, String sqlStr) throws SQLException {
- Pagination pagination = new Pagination(sqlStr);
- pagination.setTotalRows(getTotalRowCount(sqlStr));
- pagination.setCurrentPage(pageIndex);
- return pagination;
- }
- }
内部类Pagination的构造函数需要传入一个字符串参数,这是因为需要将数据库查询语句传入到该类中,用于后期拼装成带有limit关键字的分页查询语句。
BaseDao类提供了一个公开方法getPagination(int pageIndex, String sqlStr)用于获取该接口实例。该方法的两个参数分别是需要查询的页码和用于查询数据的SQL语句。
有些细心的读者可能发现了,每次调用getPagination方法时,其中的pagination.setTotalRows(getTotalRowCount(sqlStr))语句也总会被执行,这个显得有些多余,因为第一次调用getPagination方法时,就已经计算过数据的总行数及总页数,没有必要每次调用都重新计算一次。
但是请大家停下来思考一下,我们设计的是Web程序,当某个用户在查看数据时,系统反馈回来的数据量是N,如果此时恰巧有另一个用户正在向服务器提交新的数据,这时数据量已变成N+1了。那么如果我们只在程序最初读取一次数据总行数、计算总页数,就无法正确的将最新的数据分页信息推送到客户端,导致第一个用户看到的数据与服务器上真实的数据不匹配。
经过上述设计,在Controller中处理客户的数据请求,就变得非常轻松简洁了。客户只要将请求的数据页码作为URL参数传入到Controller中即可获得相应的数据。
- @RequestMapping(value = "/{pageIndex}", method = RequestMethod.GET)
- public String list(@PathVariable int pageIndex, Model model) {
- IPagination<Communicator> page = getCommunicatorService().pagingQuery(pageIndex);
- List<Communicator> list = page.getData();
- model.addAttribute("pageIndex", page.getCurrentPage());
- model.addAttribute("totalPages", page.getTotalPages());
- model.addAttribute("totalRows", page.getTotalRows());
- model.addAttribute("list", list);
- return "communicator/list";
- }
- Java数据分页
- java分页设计-支持海量数据分页
- java分页设计-支持海量数据分页
- Java数据分页通用封装
- java分页数据导出excel
- Java jsp 分页显示数据
- java分页数据导出excel
- java类实现大规模数据的分页
- Java中数据分页显示之PageBean
- Java中数据分页显示之HibernateTemplate
- POI 导出 java客户端 支持数据分页.
- java web实现分页显示数据
- 分页显示数据的java工具类
- java数据列表 分页对象(未测试)
- java ajax 上拉加载数据/分页
- java使用多线程进行分页数据采集
- Java实现分页数据获取CachedRowSet
- MySQl + java-web 数据分页案例
- 自封闭的html标签
- POJ 2484 A Funny Game
- linux split命令使用简介
- java面试题十九 判断题
- 开篇博客
- Java数据分页
- centos下ftp服务器搭建实践
- 循环队列的注意要点
- 10、linux中系统时间
- <leetcode系列> Path Sum
- 【算法分析】排序算法:鸡尾酒排序
- 自定义view学习系列之圆形进度条
- 黑马程序员——Java基础语法
- ./在Linux下的含义解读