虽然现在有很多框架可以很简单的实现分页这个功能,但是我觉的对于初学者来说,还是很有必要学习自己手动实现分页这个功能,更好的理解分页的原理,编程最重要的就是思想.
1 思路分析
在写代码之前我们得分析怎样实现分页,把思路分析清楚了,写起代码来才能游刃有余.
我们来看看分页的效果.
这就是我们要实现的效果
从效果图中我们可以看到,要把数据按照我们希望的方式展示,我么能的到的数据有
totalPage 总页数
currentPage 当前页数
pageSize 每页显示的数据条数
totalSize 总的数据条数
还有就是每页显示的数据条数,是需要我们从后台时时传过来的,那我们可以把这些每页显示的数据封装到一个JavaBean中,然后把它存到域对象中,最后展示到前端页面.
如果上面的这些数据我们一个一个的从后台去取的话,操作起来太麻烦了,于是我们想,前台不就是需要这些数据吗?那我们可以把这些数据有封装到一个PageBean对象身上,到时候把它页存到域对象中,然后到jsp页面展示出来不就行了吗,对吧?
List 用于存放每页要显示的数据的list
创建Product Bean用来封装从数据库中的到的数据
package bean;
import java.io.Serializable;
import java.util.Date;
public class Product implements Serializable{
private static final long serialVersionUID = 2078432725470146037L;
//商品pid
private String pid;
//商品名称
private String pname;
//市场价格
private Double market_price;
//商城价格
private Double shop_price;
//商品图片
private String pimage;
//商品上架时间
private String pdate;
//商品描述
private String pdesc;
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public Double getMarket_price() {
return market_price;
}
public void setMarket_price(Double market_price) {
this.market_price = market_price;
}
public Double getShop_price() {
return shop_price;
}
public void setShop_price(Double shop_price) {
this.shop_price = shop_price;
}
public String getPimage() {
return pimage;
}
public void setPimage(String pimage) {
this.pimage = pimage;
}
public String getPdate() {
return pdate;
}
public void setPdate(String pdate) {
this.pdate = pdate;
}
public String getPdesc() {
return pdesc;
}
public void setPdesc(String pdesc) {
this.pdesc = pdesc;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return "Product [pid=" + pid + ", pname=" + pname + ", market_price=" + market_price + ", shop_price="
+ shop_price + ", pimage=" + pimage + ", pdate=" + pdate + ", pdesc=" + pdesc + "]";
}
}
创建PageBean用来封装前台页面需要的数据
package bean;
import java.io.Serializable;
import java.util.List;
public class PageBean implements Serializable{
private static final long serialVersionUID = 1L;
//当页要显示的商品集合
private List<Product> list;
//当前页数
private int currentPage;
//每页显示数据条数
private int pageSize;
//总页数
private int totalPage;
//总条数
private Long totalSize;
//每页要显示的数据
public List<Product> getList() {
return list;
}
public void setList(List<Product> list) {
this.list = list;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public Long getTotalSize() {
return totalSize;
}
public void setTotalSize(Long totalSize) {
this.totalSize = totalSize;
}
@Override
public String toString() {
return "PageBean [list=" + list + ", currentPage=" + currentPage + ", pageSize=" + pageSize + ", totalPage="
+ totalPage + ", totalSize=" + totalSize + "]";
}
}
在Dao层操作数据库,完成数据的封装
package dao;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import com.sun.org.apache.bcel.internal.generic.NEW;
import bean.Product;
import utils.C3P0Utils;
import utils.DBUtils;
import utils.TransctionManager;
public class ProductDao {
/**
* 每页显示的商品集合
*
* @param pageSize
* @param currentPage
* @throws SQLException
*/
public List<Product> pageProduct(int currentPage, int pageSize) throws SQLException {
int a = (currentPage - 1) * pageSize;
QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource());
String sql = "select * from product limit ?,?";
List<Product> list = runner.query(sql, new BeanListHandler<>(Product.class),a,pageSize);
return list;
}
/**
* 查询总记录数
*/
public Long queryProductCount() throws SQLException {
QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource());
String sql = "select count(*) from product";
Long count = (Long) runner.query(sql, new ScalarHandler());
return count;
}
}
在Service层完成业务逻辑
package service;
import java.sql.SQLException;
import java.util.List;
import bean.PageBean;
import bean.Product;
import dao.ProductDao;
import utils.TransctionManager;
public class ProductService {
/**
* 分页查询所有商品
*/
public PageBean pageQuery(int currentPage) {
// 封装PageBean
/**
* 分别获取到: 当前页 每页显示数据条数 每页要显示的商品集合 总页数 总数据条数
*/
PageBean bean = new PageBean();
// 当前页
bean.setCurrentPage(currentPage);
// 每页显示数据条数
int pageSize = 5;
bean.setPageSize(pageSize);
// 每页要显示的商品集合
ProductDao dao = new ProductDao();
try {
List<Product> products = dao.pageProduct(currentPage, pageSize);
bean.setList(products);
// 总数据条数
Long totalSize = dao.queryProductCount();
bean.setTotalSize(totalSize);
// 总页数
int totalPage = 0;
totalPage = (int) (totalSize / pageSize);
if (totalSize % pageSize != 0) {
totalPage++;
}
bean.setTotalPage(totalPage);
} catch (SQLException e) {
e.printStackTrace();
}
return bean;
}
}
在Servlet中编写控制代码
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.beanutils.BeanUtils;
import bean.PageBean;
import bean.Product;
import service.ProductService;
import utils.DateUtil;
/**
* 用来处理分页的Servlet,利用反射技术实现动态获取前台调用的方法,这里使用反射技术是为了增 强代码的可维护性.
如果不使用反射技术,那么我们要么只能时针对前台的每个请求我们都写一个Servlet,或者使用if{}else if 来判断前台传过来的方法名
*/
public class BigServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 解决中文乱码问题
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String methods = request.getParameter("method");
//通过反射技术获取servlet字节码文件对象,创建对象然后调用方法,注意:一个servlet只有只有一个对象
Class<? extends BigServlet> clazz = this.getClass();
try {
Method method = clazz.getMethod(methods, HttpServletRequest.class,HttpServletResponse.class);
method.invoke(this,request,response);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
/**
* 分页查询
*/
public void page(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
//获取请求参数
int currentPage = Integer.parseInt(request.getParameter("currentPage"));
//调用业务层的方法获取pageBean
ProductService service=new ProductService();
PageBean bean=service.pageQuery(currentPage);
//把数据存入域对象中
HttpSession session = request.getSession();
session.setAttribute("page",bean);
response.sendRedirect(request.getContextPath()+"/page.jsp");
}
}
C3P0连接池工具类
package utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Utils {
/**
* c3p0连接池工具类
*/
private static ComboPooledDataSource dataSource;
static {
dataSource=new ComboPooledDataSource();
}
public static DataSource getDataSource() {
return dataSource;
}
public static Connection getConnection() {
Connection con=null;
try {
con= dataSource.getConnection();
} catch (SQLException e) {
System.out.println(e);
throw new RuntimeException("数据库链接异常,请联系技术人员");
}
return con;
}
public static void close(ResultSet resultSet,PreparedStatement ps,Connection con) {
if (resultSet!=null) {
try {
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
}
if (ps!=null) {
try {
ps.close();
} catch (Exception e) {
System.out.println(e);
}
}
if (con!=null) {
try {
con.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
}
用于封装数据的工具类(这个可以用commons-dbutils-1.4.jar代替),这个工具类可以自己封装,新手建议自己封装一下
package utils;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.management.RuntimeErrorException;
import javax.sql.DataSource;
public class DBUtils {
/**
* 操作数据库的工具类
*/
private DataSource dataSource;
public DBUtils(DataSource dataSource) {
this.dataSource = dataSource;
}
// 增删改方法
public int update(String sql, Object[] params) {
PreparedStatement ps = null;
int num = 0;
// 注册驱动,获得链接
Connection con = C3P0Utils.getConnection();
// 获取执行对象
try {
ps = con.prepareStatement(sql);
// 设置?处参数
if (params != null) {
for (int i = 1; i <= params.length; i++) {
ps.setObject(i, params[i - 1]);
}
}
num = ps.executeUpdate();
} catch (SQLException e) {
System.out.println(e);
throw new RuntimeException("数据库链接异常,请联系技术人员");
}
// 关闭资源
C3P0Utils.close(null, ps, con);
return num;
}
// 操作数据实现查询多条数据功能
public <E> List<E> query(String sql, Object[] params, Class<E> clazz) {
PreparedStatement ps = null;
List<E> list = null;
ResultSet set = null;
// 1注册驱动,获取链接
Connection con = C3P0Utils.getConnection();
try {
// 2后去执行对象
ps = con.prepareStatement(sql);
// 设置?处参数
if (params != null) {
for (int i = 1; i <= params.length; i++) {
ps.setObject(i, params[i - 1]);
}
}
// 利用ps动态获取所有字段名
ResultSetMetaData metaData = ps.getMetaData();
// 利用字节码文件对象获取clazz对象中所有的方法
Method[] methods = clazz.getMethods();
// 3执行sql语句,处理结果集
set = ps.executeQuery();
// 遍历结果集,并把结果集封装到对象中,再把每对象封装到集合中
// 创建一个集合用于封装每个对象
list = new ArrayList<>();
while (set.next()) {
// 创建对象用于封装每条数据
E e = clazz.newInstance();
// 遍历metaData获取每一个字段名
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i);
// 根据列明,获取每一列数据
Object object = set.getObject(columnName);
// 遍历所有方法,找出setxx()的方法
for (Method method : methods) {
String name = method.getName();
if (name.equalsIgnoreCase("set" + columnName)) {
method.invoke(e, object);
}
}
}
list.add(e);
}
} catch (Exception e) {
System.out.println(e);
throw new RuntimeException("数据库链接异常,请联系技术人员");
}
// 关闭资源
C3P0Utils.close(set, ps, con);
return list;
}
// 操作数据实现查询一条数据
public <E> E queryBean(String sql, Object[] params, Class clazz) {
PreparedStatement ps = null;
ResultSet resultSet = null;
E e = null;
// 1注册驱动,获得链接
Connection con = C3P0Utils.getConnection();
// 2获取执行对象
try {
ps = con.prepareStatement(sql);
// 设置?处参数
if (params != null) {
for (int i = 1; i <= params.length; i++) {
ps.setObject(i, params[i - 1]);
}
}
// 动态获取ps字段集
ResultSetMetaData metaData = ps.getMetaData();
// 同字节码文件对象获取所有方法
Method[] methods = clazz.getMethods();
// 3.执行sql语句
resultSet = ps.executeQuery();
// 4.处理结果集,把数据封装到JavaBean对象中
while (resultSet.next()) {
// 创建用于封装数据JavaBean对象
e = (E) clazz.newInstance();
// 获取每一列列明
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i);
// 获取每一列数据
Object object = resultSet.getObject(columnName);
// 遍历所有方法,找出setxxx的方法,然后为每个字段赋值
for (Method method : methods) {
String name = method.getName();
if (name.equalsIgnoreCase("set" + columnName)) {
method.invoke(e, object);
}
}
}
}
} catch (Exception ex) {
System.out.println(ex);
throw new RuntimeException("数据读取异常,请联系技术人员");
}
// 5.释放资源
C3P0Utils.close(resultSet, ps, con);
return e;
}
// 实现操作一个字段的查询
public Object queryScalar(String sql, Object[] params) {
PreparedStatement ps = null;
ResultSet resultSet = null;
Object object=null;
// 1注册驱动,获得链接
Connection con = C3P0Utils.getConnection();
// 2获取执行对象
try {
ps = con.prepareStatement(sql);
// 设置?处参数
if (params != null) {
for (int i = 1; i <= params.length; i++) {
ps.setObject(i, params[i - 1]);
}
}
// 动态获取ps字段集
ResultSetMetaData metaData = ps.getMetaData();
//3.执行sql语句
resultSet = ps.executeQuery();
//4.处理结果集,获取到每一个字段对应的值
while (resultSet.next()) {
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i);
object = resultSet.getObject(columnName);
}
}
}catch (Exception e) {
System.out.println(e);
}
//5.释放资源
C3P0Utils.close(resultSet, ps, con);
return object;
}
}
连接池配置文件
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///数据库名</property>
</default-config>
</c3p0-config>
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Day22_1</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description></description>
<display-name>BigServlet</display-name>
<servlet-name>BigServlet</servlet-name>
<servlet-class>servlet.BigServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BigServlet</servlet-name>
<url-pattern>/big</url-pattern>
</servlet-mapping>
</web-app>
前台页面 page.jsp
因为我们把我们需要的数据都封装到了PageBean对象中,PageBean对象又在session中,我们只需要取出域对象中的数据放在我们需要放的地方即可.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h5>欢迎使用~</h5>
<table border="1" cellspacing="0" align="center" width="80%">
<tr>
<th>商品id</th>
<th>商品图片</th>
<th>商品名称</th>
<th>市场价格</th>
<th>商城价格</th>
<th>上架日期</th>
<th>商品描述</th>
</tr>
<c:forEach items="${page.list }" var="p">
<tr>
<td>${p.pid }</td>
<td><img alt="" src="${p.pimage }" width="100px" height="100px">
</td>
<td>${p.pname }</td>
<td>${p.market_price }</td>
<td>${p.shop_price }</td>
<td>${p.pdate }</td>
<td>${p.pdesc }</td>
</tr>
</c:forEach>
<tr align="center">
<td colspan="7">总共 ${page.totalPage } 页 |当前是第 ${page.currentPage }
页 | 每页显示 ${page.pageSize } 条数据|总共有 ${page.totalSize } 条数据</td>
</tr>
<tr align="center">
<td colspan="7">
<%--判断是否是首页,如果是则显示上一页,首页 --%>
<c:if test="${page.currentPage!=1 }">
<a href="${pageContext.request.contextPath }/big?method=page¤tPage=1">首页</a>
<a href="${pageContext.request.contextPath }/big?method=page¤tPage=${page.currentPage-1}">上一页</a>
</c:if>
<c:forEach begin="1" end="${page.pageSize }"
var="i">
<%--判断是否是当前页,若是当前页加以区别 --%>
<c:if test="${i==page.currentPage }">
<a>${i }</a>
</c:if>
<%--正常显示第i页 --%>
<c:if test="${i!=page.currentPage }">
<a href="${pageContext.request.contextPath }/big?method=page¤tPage=${i}">${i }</a>
</c:if>
</c:forEach>
<%--判断是否是尾页,如果是则显示下一页,尾页 --%>
<c:if test="${page.currentPage!=page.pageSize }">
<a href="${pageContext.request.contextPath }/big?method=page¤tPage=${page.currentPage+1}">下一页</a>
<a href="${pageContext.request.contextPath }/big?method=page¤tPage=${page.pageSize}">尾页</a>
</c:if>
</td>
</tr>
</table>
</body>
</html>
==温馨提醒:==
我所用的开发环境为:win10 ,tomcat7,jdk 1.8
用到类库
c3p0-0.9.1.2.jar
commons-dbutils-1.4.jar(如果自己封装dbUtil则可不用)
commons-logging-1.1.1.jar
jstl.jar
mysql-connector-java-5.1.39-bin.jar
standard.jar
==数据库数据准备==
create database page;
use page;
create table `product` (
`pid` varchar (96) primary key,
`pname` varchar (150),
`market_price` double ,
`shop_price` double ,
`pimage` varchar (600),
`pdate` date ,
`pdesc` varchar (765)
);
INSERT INTO `product` VALUES('1','小米 4c 标准版','1399','1299','products/1/c_0001.jpg','2015-11-02','小米 4c 标准版 全网通 白色 移动联通电信4G手机 双卡双待');
INSERT INTO `product` VALUES('10','华为 Ascend Mate7','2699','2599','products/1/c_0010.jpg','2015-11-02','华为 Ascend Mate7 月光银 移动4G手机 双卡双待双通6英寸高清大屏,纤薄机身,智能超八核,按压式指纹识别!!选择下方“移动老用户4G飞享合约”,无需换号,还有话费每月返还!');
INSERT INTO `product` VALUES('11','vivo X5Pro','2399','2298','products/1/c_0014.jpg','2015-11-02','移动联通双4G手机 3G运存版 极光白【购机送蓝牙耳机+蓝牙自拍杆】新升级3G运行内存·双2.5D弧面玻璃·眼球识别技术');
INSERT INTO `product` VALUES('12','努比亚(nubia)My 布拉格','1899','1799','products/1/c_0013.jpg','2015-11-02','努比亚(nubia)My 布拉格 银白 移动联通4G手机 双卡双待【嗨11,下单立减100】金属机身,快速充电!布拉格相机全新体验!');
INSERT INTO `product` VALUES('13','华为 麦芒4','2599','2499','products/1/c_0012.jpg','2015-11-02','华为 麦芒4 晨曦金 全网通版4G手机 双卡双待金属机身 2.5D弧面屏 指纹解锁 光学防抖');
INSERT INTO `product` VALUES('14','vivo X5M','1899','1799','products/1/c_0011.jpg','2015-11-02','vivo X5M 移动4G手机 双卡双待 香槟金【购机送蓝牙耳机+蓝牙自拍杆】5.0英寸大屏显示·八核双卡双待·Hi-Fi移动KTV');
INSERT INTO `product` VALUES('15','Apple iPhone 6 (A1586)','4399','4288','products/1/c_0015.jpg','2015-11-02','Apple iPhone 6 (A1586) 16GB 金色 移动联通电信4G手机长期省才是真的省!点击购机送费版,月月送话费,月月享优惠,畅享4G网络,就在联通4G!');