`
禹爸爸
  • 浏览: 87746 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

Java数据分页

阅读更多

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

 

 Service层的代码也是很简洁的

public IPagination<T> pagingQuery(int pageIndex) {
	logger.info("PageIndex: " + pageIndex);
	try {
		return this.getDao().getPagination(pageIndex, "SELECT * FROM vw_communicator");
	} catch (SQLException e) {
		e.printStackTrace();
	}
	return null;
}

 

 

  • 大小: 91.2 KB
分享到:
评论

相关推荐

    java 数据分页显示

    在Java编程中,数据分页显示是Web应用中常见的需求,尤其在处理大量数据时,为了提高用户体验并优化服务器性能,通常需要将数据分成多个页面进行展示。本篇将深入探讨Java实现数据分页显示的核心技术和策略。 首先...

    java数据分页

    #### 二、Java数据分页原理 数据分页的核心在于如何从数据库中高效地获取特定范围的数据。这主要依赖于SQL语句中的`LIMIT`关键字。通过组合`LIMIT`和`OFFSET`关键字,可以精确控制查询结果的起始位置和返回的数量,...

    displaytag.rar java数据分页驱动

    在Java Web应用中,数据分页是一种常见的需求,特别是在处理大量数据时,为了提高页面加载速度和用户体验,通常会将数据分批次显示。DisplayTag库通过集成在JSP页面中,可以轻松实现这一功能,无需编写大量的Java...

    java数据分页,JAVA入门的必备选择

    在Java编程语言中,数据分页是处理大数据集时的关键技术,尤其对于网站开发,如论坛,它能提高用户体验并优化服务器性能。本教程将深入探讨如何在JAVA中实现高效的数据分页,这对于初学者来说是必备的知识点。 首先...

    分页缓存

    本篇文章将深入探讨如何使用Java来实现分页缓存,并介绍在读取过程中如何优先从缓存获取数据。 首先,理解分页的基本概念是必要的。在Web应用中,当用户浏览大量数据时,通常会采用分页的方式来显示,而不是一次性...

    java 通用分页 java 通用分页

    Java 通用分页是指在Java编程中对大量数据进行分页处理,以提高系统效率和性能。下面对Java通用分页的知识点进行详细说明: 1. 分页的必要性 在实际项目中,数据量可能非常大,直接查询所有数据将会导致系统性能...

    Java海量数据分页Bean

    Java海量数据分页Bean, 适用于Oracle(适当修改,适用于任何数据库).功能描述:传入到达页码(具有容错性)、每页记录数、Select查询语句,返回该页所有的记录(整页是List集合,每条记录是一个 HashMap)、总行数、总...

    java动态树形菜单与分页

    在Java开发中,动态树形菜单和分页是常见的需求,尤其在构建Web应用程序时,它们对于用户界面的交互性和数据管理的效率至关重要。本文将深入探讨这两个概念以及如何在Java环境中实现它们。 首先,我们来看动态树形...

    java多线程分页查询

    ### Java多线程分页查询知识点详解 #### 一、背景与需求分析 在实际的软件开发过程中,尤其是在处理大量数据时,如何高效地进行数据查询成为了一个关键问题。例如,在一个用户众多的社交平台上,当用户需要查看...

    java动态分页类

    Java动态分页类是Web开发中常用的一种技术,主要用于处理大量数据时的显示问题,以提高用户体验和系统性能。在数据库查询中,一次性加载所有数据可能会导致内存压力过大,特别是对于大数据量的表。因此,分页技术...

    java web与Oracle数据的分页功能

    在Java Web开发中,与Oracle数据库进行交互时,分页功能是常见的需求,尤其是在处理大量数据时,为了提高用户体验和加载速度,分批次地显示数据是必不可少的。本项目提供的源代码正是针对这一需求,提供了在Java Web...

    java几种分页方法

    java几种分页方法java几种分页方法java几种分页方法 java几种分页方法java几种分页方法java几种分页方法 java几种分页方法java几种分页方法java几种分页方法 java几种分页方法java几种分页方法java几种分页方法

    java万能分页代码

    Java万能分页代码是Java开发中常用的工具,尤其在处理大数据量的Web应用时,分页技术能够显著提升用户体验并优化服务器性能。这个3.0版本的分页代码库通常包含了一些经过优化的分页算法和接口,适用于各种场景,如...

    java通用分页含使用文档

    Java通用分页是一个常见且重要的技术点,尤其在开发大型数据集展示的Web应用时,分页能够有效地提高用户体验并优化服务器性能。本资源提供的"java通用分页含使用文档"是一个jar包,旨在简化Java Web开发中的分页实现...

    最好最好的java万能分页标签

    Java万能分页标签是一种高效的页面分页解决方案,尤其适合于Java Web开发中处理大量数据的展示场景。这种标签库简化了开发过程,使得开发者无需编写繁琐的分页逻辑,而是通过简单的配置和调用就能实现高效且灵活的...

    jsp java自定义标签 分页 当前位置 循环遍历数据

    本文将深入探讨如何使用Java自定义标签来实现分页功能,当前位置的显示以及数据的循环遍历。 首先,让我们了解一下Java自定义标签的基本概念。自定义标签是JSP的一种扩展机制,它允许开发者创建自己的标签库,这些...

    JAVA大数据分页算法

    下面将详细探讨Java中大数据分页算法的相关知识点。 首先,我们需要理解分页的基本概念。分页是将大型数据集分割成小块,每一块称为一页,用户可以逐页浏览,而不是一次性看到全部数据。在Web应用中,这通常体现在...

    java通用分页代码实例.rar

    Java 分页技术是Java开发中常见的一种数据处理方式,尤其在大数据量的Web应用中,为了提高用户体验并减轻服务器压力,通常需要实现分页显示功能。这个"java通用分页代码实例"提供了一种适用于任意数据库的解决方案,...

    java简单分页.txt

    ### Java简单分页技术解析 #### 一、引言 在现代软件开发中,特别是Web应用领域,数据展示经常需要采用分页的方式处理大量...通过掌握这些知识点,开发者可以更加高效地完成数据分页任务,提升Web应用的用户体验。

    用JSP进行数据分页显示的一个实现.rar_java 分页_java 分页显示_jsp 显示数据_jsp数据分页_分页显示

    在Java Web开发中,数据分页是常见的需求,特别是在处理大量数据时,为了提高用户体验,我们需要将数据分批次展示,而不是一次性加载所有记录。这里我们关注的是如何利用JSP(JavaServer Pages)来实现数据的分页...

Global site tag (gtag.js) - Google Analytics