1、通用分页概述(基于MySql数据库的通用分页)
通用分页就是把我们经常使用的分页方法进行封装,然后用泛型传递对象,利用反射获取对象的所有属性并且赋值。
2、为何要使用通用分页?
一般在实际项目的开发过程中我们会有很多数据的展示,并且数据量大,展示的数据种类繁多,这意味着我们要写很多重复的代码,我们可将重复相同的代码提取出来进行封装,后期进行调用即可。
3、通用分页核心思路:将上一次查询请求再发一次,只不过页码变了
4、环境配置(如何实现分页)
1、明确我们的需求是什么?
2、在实际项目开发中分页需要哪些参数?页码(每页所展示的数据)、总记录数、是否分页、点击下一页或者上一页所需地址
PageBean 分页三要素 page 页码 视图层传递过来 rows 页大小 视图层传递过来 total 总记录数 后台查出来 pagination 是否分页 视图层传递过来 getStartIndex() 基于MySql数据库分页,获取分页开始标记
4.1.以书籍为例,创建一个书籍表,同时再创建一个书籍项目
create table t_book(
book_id int not null primary key auto_increment comment '书本编号',
book_name varchar(30) not null comment '书本名称',
book_name_pinyin varchar(30) null comment '书本拼音',
book_price float default 0 comment '书本价格',
book_type varchar(10) not null comment '书本类型'
)comment '书本信息表';
#分页(limit)
#limit 参数1,参数2 (page-1)*rows
#参数1:从第几条数据开始
#参数2:每次返回多少条数据 rows
#第一页:从0开始,返回2条
#第二页:从2开始,返回2条
4.2.项目中的相关配置以及导入必要的依赖
依赖:
mysql-connector-java-5.1.44-bin.jar MySQL的驱动类
pinyin4j-2.5.0.jar 拼音的jar,作用是可以快速的将中文转变成英文,只提取拼音首字母
standard-1.1.2.jar jstl标签
jstl-1.2.jar
工具类:
CommonUtils.java 反射封装ResultSet结果集
DBHelper.java 连接数据库
config.properties 资源文件
EncodingFiter.java 中文乱码过滤器
StringUtils.java 判断字符串是否存在(为空)
PageTag.java 自定义标签的助手类
PageBean.java 分页工具类PinYinUtil.java 拼音工具类
4.3.先写一个分页工具类,用于实现分页效果。
package com.zking.pagination.util;
import java.io.Serializable;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
/**
* 分页工具类
* @author gss
*
*/
public class PageBean implements Serializable{
//当前页码,默认1
private int page=1;
//页面显示条数,默认10
private int rows=10;
//总记录数,默认0
private int total=0;
//是否分页标记,默认分页
private boolean pageination=true;
//http://localhost:8080/pagination/BookServlet.action
///pagination/BookServlet.action
//获取上一次请求路径
private String url;
//获取上一次请求的请求参数集合
// Map<String,String[]> params=req.getParameterMap();
// <input type="text" name="bookname" value="1"/>
private Map<String,String[]> params;
public PageBean() {
super();
}
/**
* 获取起始记录的下标(基于Mysql分页,计算limit分页开始位置)
* @return
*/
public int getStartIndex() {
return (this.page-1)*this.rows;
}
public void setRequest(HttpServletRequest req) {
//获取前端提交的请求参数(分页三要素:页码、页大小、总记录数 )
String page = req.getParameter("page");
String rows = req.getParameter("rows");
String pagination = req.getParameter("pagination");
//赋值
this.setPage(page);
this.setRows(rows);
this.setPageination(pagination);
//获取上一次请求的请求路径
//http://localhost:8080/pagination/BookServlet.action
// req.getRequestURI()==等同于/pagination/BookServlet.action
// req.getContextPath()==项目名/pagination
// req.getServletPath()==请求路径/BookServlet.action
//当前路径
this.url = req.getRequestURI();
//获取上一次请求的请求参数集合
//Map<String,String[]>-->params.put("bookname",new String[]{"",""})
this.params = req.getParameterMap();
}
/**
* 获取最大页码
* @return
*/
public int getMaxPager() {
int p = this.total/this.rows;
//判断是否有余数,如果有余数,则最大页码+1
if(this.total%this.rows!=0)
p++;
return p;
}
/**
* 获取上一页
* @return
*/
public int getProviousPager() {
int p = this.page-1;
if(p<1)
p=1;
return p;
}
/**
* 获取下一页
* @return
*/
public int getNextPager() {
int p = this.page+1;
if(this.page>=this.getMaxPager())
p=this.getMaxPager();
return p;
}
public void setPage(String page) {
if(null!=page)
this.page = Integer.parseInt(page);
}
public void setRows(String rows) {
if(null!=rows)
this.rows = Integer.parseInt(rows);
}
public void setPageination(String pageination) {
if(null!=pageination)
this.pageination = Boolean.parseBoolean(pageination);
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public boolean isPageination() {
return pageination;
}
public void setPageination(boolean pageination) {
this.pageination = pageination;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, String[]> getParams() {
return params;
}
public void setParams(Map<String, String[]> params) {
this.params = params;
}
@Override
public String toString() {
return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pageination=" + pageination
+ ", url=" + url + ", params=" + params + "]";
}
}
BaseDao.java(是一个通用的父类方法,来帮助我们实现通用的分类)
package com.zking.pagination.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import com.zking.pagination.util.DBHelper;
import com.zking.pagination.util.PageBean;
public class BaseDao<T> {
public static interface CallBack<K>{
//只用于遍历ResultSet结果集
public List<K> foreachRs(ResultSet rs) throws SQLException;
}
/**
* 通用分页方法(既支持分页,也支持不分页)
* @param sql 普通的sql
* @param pageBean pageBean 分页对象
* @return 查询结果集
*/
public List<T> excuteQuery(String sql,PageBean pageBean,CallBack<T> callBack){
//声明对象
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
//获取连接对象
conn = DBHelper.getConnection();
//根据pageBean分页对象判断是否分页
if(null!=pageBean&&pageBean.isPageination()) {
//1.分页
//1)根据满足条件查询总记录数
String countSQL = this.getCountSQL(sql);
//创建执行对象
stmt = conn.prepareStatement(countSQL);
//执行sql语句并返回总记录数
rs = stmt.executeQuery();
//获取总记录数
if(rs.next()) {
pageBean.setTotal(rs.getInt(1));
}
//2)根据满足条件查询分页结果集
sql = this.getPagerSQL(sql, pageBean);//覆盖
}
//创建执行对象
stmt = conn.prepareStatement(sql);
//查询结果集
rs = stmt.executeQuery();
return callBack.foreachRs(rs);
} catch (Exception e) {
e.printStackTrace();
}finally {
DBHelper.close(conn, stmt, rs);
}
return null;
}
/**
* 将普通的sql语句转换成查询总记录条数的sql语句
* select * from t_book
* select * from t_book where ...
* select book_id,book_name from t_book
* select book_id,book_name from t_book where ...
* ------------>
* select count(0) from t_book where ...
* @param sql 普通的sql
* @return 查询总记录数的sql
*/
private String getCountSQL(String sql) {
return "select count(0) from ("+sql+") temp";
}
/**
* 将普通的sql语句转换成查询分页结果集的sql语句
* select * from t_book
* select * from t_book where ...
* select book_id,book_name from t_book
* select book_id,book_name from t_book where ...
* ------------>
* select * from t_book limit
* select * from t_book where 1=1 limit
* @param sql 普通的sql
* @param pageBean 分页对象(包含当前页码和每页条数,用于计算分页的关键数据)
* @return 查询分页结果集的sql
*/
private String getPagerSQL(String sql,PageBean pageBean) {
return sql+" limit "+pageBean.getStartIndex()+","+pageBean.getRows();
}
}
BookDao.java(dao类继承了BaseDao 父类,代码简洁,通用的方法都在父类中)
package com.zking.pagination.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.management.RuntimeErrorException;
import com.zking.pagination.entity.Book;
import com.zking.pagination.util.DBHelper;
import com.zking.pagination.util.PageBean;
import com.zking.pagination.util.StringUtils;
public class BookDao extends BaseDao<Book>{
//1、书本新增
public void addBook(Book book) {
//声明对象
Connection conn = null;
PreparedStatement stmt = null;
try {
//获取连接对象
conn= DBHelper.getConnection();
//定义sql语句
String sql = "insert into t_book(book_name,book_name_pinyin,book_price,book_type) values(?,?,?,?)";
//创建执行对象
stmt = conn.prepareStatement(sql);
//参数赋值
stmt.setString(1, book.getBook_name());
stmt.setString(2, book.getBook_name_pinyin());
stmt.setFloat(3, book.getBook_price());
stmt.setString(4, book.getBook_type());
//执行sql语句并返回影响行数
int i = stmt.executeUpdate();
if(i<1) {
throw new RuntimeException("影响行数为0,添加失败");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DBHelper.close(conn, stmt, null);
}
}
/**
* 2、书本分页查询 query/find/select/get
* @param book 要查询的对象
* @return 返回结果集
*/
public List<Book> queryBookPager(Book book,PageBean pageBean){
//定义sql语句
String sql = "select book_id,book_name,book_name_pinyin,book_price,book_type from t_book where 1=1";
//拼接查询条件,按照书本名称模糊查询
if(StringUtils.isNotBlank(book.getBook_name()))
sql+=" and book_name like '%"+book.getBook_name()+"%'";
//按照书本编号降序排序
sql+=" order by book_id desc";
System.out.println(sql);
return super.excuteQuery(sql, pageBean, new CallBack<Book>() {
@Override
public List<Book> foreachRs(ResultSet rs) throws SQLException {
List<Book> blist = new ArrayList<>();
//定义Book对象
Book b = null;
//循环遍历结果集
while(rs.next()) {
//创建Book对象
b = new Book();
b.setBook_id(rs.getInt("book_id"));
b.setBook_name(rs.getString("book_name"));
b.setBook_name_pinyin(rs.getString("book_name_pinyin"));
b.setBook_price(rs.getFloat("book_price"));
b.setBook_type(rs.getString("book_type"));
//将对象添加到集合中
blist.add(b);
}
return blist;
}
});
//以下为查询原生态代码
/* List<Book> blist = new ArrayList<>();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
//获取连接对象
conn= DBHelper.getConnection();
//定义sql语句
String sql = "select book_id,book_name,book_name_pinyin,book_price,book_type from t_book where 1=1";
//拼接查询条件,按照书本名称模糊查询
if(StringUtils.isNotBlank(book.getBook_name()))
sql+=" and book_name like '%"+book.getBook_name()+"%'";
//按照书本编号降序排序
sql+=" order by book_id desc";
System.out.println(sql);
//创建执行对象
stmt = conn.prepareStatement(sql);
//执行sql语句并返回查询结果集
rs = stmt.executeQuery();
//定义Book对象
Book b = null;
//循环遍历结果集
while(rs.next()) {
//创建Book对象
b = new Book();
b.setBook_id(rs.getInt("book_id"));
b.setBook_name(rs.getString("book_name"));
b.setBook_name_pinyin(rs.getString("book_name_pinyin"));
b.setBook_price(rs.getFloat("book_price"));
b.setBook_type(rs.getString("book_type"));
//将对象添加到集合中
blist.add(b);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DBHelper.close(conn, stmt, rs);
}
return blist;*/
}
}
BookDaoTest.java 使用Junit测试
package com.zking.pagination.dao;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.zking.pagination.entity.Book;
import com.zking.pagination.util.PageBean;
import com.zking.pagination.util.PinYinUtil;
public class BookDaoTest {
Book book = null;
BookDao bd = new BookDao();
@Before
public void setUp() throws Exception {
//初始化一次
book = new Book();
//System.out.println("setUp");
}
@After
public void tearDown() throws Exception {
//System.out.println("tearDown");
}
@Test
public void testAddBook() {
for (int i = 0; i < 64; i++) {
book = new Book();
book.setBook_name("红楼梦第"+(i+1)+"章");
book.setBook_name_pinyin(PinYinUtil.toPinyin("红楼梦第"+(i+1)+"章").toLowerCase());
book.setBook_price(128f);
book.setBook_type("文学");
//调用dao类添加方法
bd.addBook(book);
}
//System.out.println("testAddBook");
}
@Test
public void testQueryBookPager() {
//模糊查询
book.setBook_name("2");
PageBean pageBean = new PageBean();
pageBean.setPage(2);//第二页
pageBean.setRows(8);//每页条数
//pageBean.setPageination(false);//不分页
//调用dao类查询方法
List<Book> books = bd.queryBookPager(book,pageBean);
System.out.println("总记录数:"+pageBean.getTotal());
books.forEach(System.out::println);
}
}
BookServlet.java
package com.zking.pagination.action;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.zking.pagination.dao.BookDao;
import com.zking.pagination.entity.Book;
import com.zking.pagination.util.PageBean;
public class BookServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求查询参数
String bookname = req.getParameter("bookname");
//实例化BookDao
BookDao bd = new BookDao();
//实例化Book
Book book = new Book();
book.setBook_name(bookname);
//创建PageBean
PageBean pageBean = new PageBean();
//必须在查询之前完成请求参数赋值
pageBean.setRequest(req);
//实现书本查询
List<Book> books = bd.queryBookPager(book,pageBean);
//将查询的结果books保存到request作用域中
req.setAttribute("books", books);
//将pageBean对象存入到request作用域中
req.setAttribute("pageBean", pageBean);
//转发到指定页面并显示查询结果
req.getRequestDispatcher("/bookList.jsp").forward(req, resp);;
}
}
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>pagination</display-name>
<!-- 配置中文乱码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.zking.pagination.util.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置servlet -->
<servlet>
<servlet-name>BookServlet</servlet-name>
<servlet-class>com.zking.pagination.action.BookServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BookServlet</servlet-name>
<url-pattern>/BookServlet.action</url-pattern>
</servlet-mapping>
</web-app>
z标签:是我们的分页标签它还有一个主要的类-----PaginationTag助手类,是整个分页的灵魂所在。
package com.zking.pagination.tag;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
import com.zking.pagination.util.PageBean;
public class PaginationTag extends BodyTagSupport {
private PageBean pageBean;
@Override
public int doEndTag() throws JspException {
return EVAL_PAGE;
}
@Override
public int doStartTag() throws JspException {
JspWriter out = pageContext.getOut();
try {
out.write(toHtml());
} catch (Exception e) {
e.printStackTrace();
}
return SKIP_BODY;
}
private String toHtml() {
//判断是否分页
if(null==pageBean||!pageBean.isPageination())
return "";
else {
StringBuilder sb=new StringBuilder();
//TODO
sb.append("<div style=\"float:right\">");
//拼接Form表单
sb.append("<form id=\"pageBeanForm\" action=\""+pageBean.getUrl()+"\" method=\"post\">");
//设置page隐藏域
sb.append("<input type=\"hidden\" name=\"page\"/>");
//拼接请求参数集合
Map<String, String[]> map = pageBean.getParams();
//获取请求参数集合键值对
Set<Entry<String,String[]>> entrySet = map.entrySet();
//遍历请求参数键值对
for (Entry<String, String[]> entry : entrySet) {
//获取请求参数名,也就是来自于表单中的name属性名称
String name=entry.getKey();
//如果参数为page,则continue跳过
if(name.equals("page"))
continue;
//获取请求参数对应的值,String[]
String[] values=entry.getValue();
//遍历value值
for (String value : values) {
//拼接请求参数
sb.append("<input type='hidden' name='"+name+"' value='"+value+"'/>");
}
}
sb.append("</form>");
//拼接共几页/第几页
sb.append("[第"+pageBean.getPage()+"页/共"+pageBean.getMaxPager()+"页]");
//拼接首页、上一页、下一页、末页
if(pageBean.getPage()==1)
sb.append("首页 上一页 ");
else {
sb.append("<a href=\"javascript:gotoPage(1)\">首页</a> ");
sb.append("<a href=\"javascript:gotoPage("+pageBean.getProviousPager()+")\">上一页</a> ");
}
if(pageBean.getPage()==pageBean.getMaxPager())
sb.append("下一页 末页 ");
else {
sb.append("<a href=\"javascript:gotoPage("+pageBean.getNextPager()+")\">下一页</a> ");
sb.append("<a href=\"javascript:gotoPage("+pageBean.getMaxPager()+")\">末页</a> ");
}
//拼接跳转页码
sb.append("<input type=\"text\" id=\"p\" style=\"width:20px;\"/>");
sb.append("<input type=\"button\" value=\"GO\" onclick=\"javascript:skipPage();\"/>");
//拼接javascript跳转方法
sb.append("<script type=\"text/javascript\">\r\n" +
"function gotoPage(page){\r\n" +
" document.getElementById(\"pageBeanForm\").page.value=page;\r\n" +
" document.getElementById(\"pageBeanForm\").submit();\r\n" +
"}");
sb.append("function skipPage(){\r\n" +
" var page=document.getElementById(\"p\").value;\r\n" +
" if(isNaN(page)||page<1||page>="+pageBean.getMaxPager()+"){\r\n" +
" alert('请输入1~"+pageBean.getMaxPager()+"之间数字!');\r\n" +
" return false;\r\n" +
" }\r\n" +
" gotoPage(page);\r\n" +
"}\r\n" +
"</script>");
sb.append("</div>");
return sb.toString();
}
}
public PageBean getPageBean() {
return pageBean;
}
public void setPageBean(PageBean pageBean) {
this.pageBean = pageBean;
}
}
启动Tomcat
bookList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="/zking" prefix="z" %>
<!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>
<!-- <script type="text/javascript">
function gotoPage(p) {
document.getElementById('ff').page.value=p;
//javascript的表单提交
document.getElementById('ff').submit();
}
function skipPage(max) {
var page=document.getElementById('go').value;
if(isNaN(page)||page<1||page>max){//判断非数字
alert('请输入1~'+max+'之间的数字!');
return false;
}
gotoPage(page);
}
</script> -->
</head>
<body>
<form action="BookServlet.action" method="post" align="center">
<label>书本名称:</label><input type="text" name="bookname"/>
<input type="submit" value="查询"/>
</form>
<table width="100%" border="1" cellpadding="0" cellspacing="0">
<tr>
<th>书本编号</th>
<th>书本名称</th>
<th>书本拼音</th>
<th>书本价格</th>
<th>书本类型</th>
</tr>
<c:forEach items="${books }" var="b">
<tr>
<th>${b.book_id }</th>
<th>${b.book_name }</th>
<th>${b.book_name_pinyin }</th>
<th>${b.book_price }</th>
<th>${b.book_type }</th>
</tr>
</c:forEach>
</table>
<z:pagination pageBean="${pageBean }"/>
<%-- <div style="float: right;">
<form id="ff" action="${pageBean.getUrl()}" method="post">
<input type="hidden" name="page"/>
<!-- 在此处应该将pageBean中的params属性获取并遍历到当前位置,动态生成input="hidden" -->
Map<String,String[]> maps = ${pageBean.getParams() }
<!--循环遍历 -->
<input type="hidden" name="bookname" value="1"/>
</form>
[第${pageBean.getPage()}页/共${pageBean.getMaxPager()}页]
<a href="javascript:gotoPage(1);">首页</a>
<a href="javascript:gotoPage(${pageBean.getProviousPager()});">上一页</a>
<a href="javascript:gotoPage(${pageBean.getNextPager()});">下一页</a>
<a href="javascript:gotoPage(${pageBean.getMaxPager()});">末页</a>
<input type="text" id="go" style="width:18px"/>
<input type="button" value="GO" onclick="skipPage(${pageBean.getMaxPager()})" />
</div> --%>
</body>
</html>
效果图: