通用分页版本三

分页版本

版本参考链接

分页一https://blog.csdn.net/licmi/article/details/106441305
分页二https://blog.csdn.net/licmi/article/details/106461125

概念

将上一次查询请求再发一次,只不过页码变了

通用思路

相信大家跟我一样,刚开始了解通用分页时不懂,不知道哪里通用了,还是感觉代码很多。
我举个例子,假如我有4张表需要实现分页,那么我需要在每个方法类都写分页查询方法是可以实现的,那如果我有上百个表呢,那么重复的代码就很多。
所以想一个办法把共性的代码部分,构成通用性的代码。(baseDao)

package com.liuchunming.dao;

import java.lang.reflect.Field;
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 com.liuchunming.entity.Book;
import com.liuchunming.util.DBAccess;
import com.liuchunming.util.PageBean;
import com.liuchunming.util.StringUtils;

public class BaseDao<T> {
	public List<T> executeQuery(String sql, Class clz,PageBean pageBean) throws SQLException, InstantiationException, IllegalAccessException{
		List<T> list =new ArrayList<T>();
		Connection con = DBAccess.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		if(pageBean !=null&&pageBean.isPagination()) {
			//分页
			//查询满足条件的总记录数
			String countSql =getCountSql(sql);
			ps =  con.prepareStatement(countSql);
			rs = ps.executeQuery();
			if(rs.next()) {
				pageBean.setTotal(rs.getObject(1)+"");
			}
			//查询满足条件需要显示的记录
			String pageSql =getPageSql(sql,pageBean);
			ps =  con.prepareStatement(pageSql);
			rs = ps.executeQuery();
		}else {
			//不分页
			ps =  con.prepareStatement(sql);
			rs = ps.executeQuery();
		}
		
		while(rs.next()) {
			T t = (T) clz.newInstance();
			//获取对象的所有属性
			for (Field f : clz.getDeclaredFields()) {
				f.setAccessible(true);//打开权限
				f.set(t, rs.getObject(f.getName()));//给属性赋值
			}
			//添加到集合
			list.add(t);
		}
		DBAccess.close(con, ps, rs);
		return list;
	}
	
	/**
	 * 查询第几页的数据
	 * @param sql
	 * @param pageBean
	 * @return
	 */
	private String getPageSql(String sql, PageBean pageBean) {
		return  sql+" limit "+pageBean.getStartIndex()+","+pageBean.getRows()+"";
	}
	/**
	 * 根据原生sql带有的条件查询总记录数
	 * @param sql
	 * @return
	 */
	private String getCountSql(String sql) {
		return "select count(1) from ("+sql+") t";
	}
}
通用baseDao讲解

首先是泛型。为什么要用泛型,因为我不知道谁要使用我的方法,使用泛型的话,只要继承把类带上就能使用里面的方法了。

public class BaseDao<T> {
	public List<T> executeQuery(String sql, Class clz,PageBean pageBean) throws 

列如book类继承

public class BookDao extends BaseDao<Book>{

通用方法三个参数
sql就是谁继承谁传sql语句
clz指对象
pagebean是指分页的方法,获取和赋值

public List<T> executeQuery(String sql, Class clz,PageBean pageBean)

这里我们可以先写一个getCountSql
无论我们传什么Sql语句过来我们只需要进行拼接就可以获取到总记录数

private String getCountSql(String sql) {
		return "select count(1) from ("+sql+") t";
	}

用来获取到下标和 每页显示多少条

	private String getPageSql(String sql, PageBean pageBean) {
		return  sql+" limit "+pageBean.getStartIndex()+","+pageBean.getRows()+"";
	}

上面说到,谁继承就传谁的类,那么对象里的属性怎么赋值呢?
我们用到了反射

		T t = (T) clz.newInstance();
			//获取对象的所有属性
			Field[]  fa =	clz.getDeclaredFields();
			for (Field f : fa) {
				f.setAccessible(true);//打开权限
				f.set(t, rs.getObject(f.getName()));//给属性赋值
			}
			//添加到集合
			list.add(t);

这里说一个容易错误的点

	private static final long serialVersionUID = 1L;

使用反射获取属性,实体类里不要实现序列号接口(Serializable)。因为反射会获取对象的所有属性
子类实现方法

package com.liuchunming.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 com.liuchunming.entity.Book;
import com.liuchunming.util.DBAccess;
import com.liuchunming.util.PageBean;
import com.liuchunming.util.StringUtils;

public class BookDao extends BaseDao<Book>{
	
	//改装后的
	public List<Book> list(Book book,PageBean pageBean) throws SQLException, InstantiationException, IllegalAccessException{
		String sql=" select * from t_mvc_book where true";
		String bname = book.getBname();
		if(StringUtils.isNotBlank(bname)) {
			sql+=" and bname like '%"+bname+"%'";
		}
		return super.executeQuery(sql, Book.class, pageBean);
	}
}	

是不是感觉简单多了!!
然后就是分页工具类

package com.liuchunming.util;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

/**
 * 分页工具类
 *
 */
public class PageBean {

	private int page = 1;// 页码

	private int rows = 10;// 页大小

	private int total = 0;// 总记录数

	private boolean pagination = true;// 是否分页
	
	//上一次查询的url
	private String url;
	//上一次查询所携带的查询条件
	private Map<String, String[]> pageMap =new HashMap<String, String[]>();
	
	public void setRequest(HttpServletRequest req) {
		//页面传递过来的当前页
		this.setPage(req.getParameter("page"));
		//传递过来的页大小
		this.setRows(req.getParameter("rows"));
		//页面传递过来的是否分页
		this.setPagination(req.getParameter("pagination"));
		//将上一次的查询请求保留
		this.setUrl(req.getRequestURL().toString());
		//将上一次查询条件的请求进行保留
		this.setPageMap(req.getParameterMap());
	}
	private void setPagination(String pagination) {
		//只有填写了字符串false才能不分页
		this.setPagination(!"false".equals(pagination));
	}
	//重载如果不为空转为int类型
	private void setRows(String rows) {
		if(StringUtils.isNotBlank(rows))
			this.setRows(Integer.valueOf(rows));
	}
	private void setPage(String page) {
		if(StringUtils.isNotBlank(page))
			this.setPage(Integer.valueOf(page));
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public Map<String, String[]> getPageMap() {
		return pageMap;
	}

	public void setPageMap(Map<String, String[]> pageMap) {
		this.pageMap = pageMap;
	}

	public PageBean() {
		super();
	}

	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 void setTotal(String total) {
		this.total = Integer.parseInt(total);
	}

	public boolean isPagination() {
		return pagination;
	}

	public void setPagination(boolean pagination) {
		this.pagination = pagination;
	}
	/**
	 * 上一页
	 * @return
	 */
	public int getPrevPage() {
		return this.page > 1 ? this.page-1 : this.page;
	}
	
	/**
	 * 下一页
	 * @return
	 */
	public int getNextPage() {
		return this.page < getMaxPage() ? this.page +1 : this.page;
	}
	
	/**
	 * 最大页
	 * @return
	 */
	public int getMaxPage() {
		return this.total % this.rows ==0 ? this.total/this.rows : (this.total/this.rows)+1;
	}
	/**
	 * 获得起始记录的下标
	 * 
	 * @return
	 */
	public int getStartIndex() {
		return (this.page - 1) * this.rows;
	}
}

page助手类

package com.liuchunming.tag;

import java.io.IOException;
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.liuchunming.util.PageBean;

public class PageTag extends BodyTagSupport{
	private static final long serialVersionUID = 1L;
	private PageBean pageBean;
	
	public PageBean getPageBean() {
		return pageBean;
	}

	public void setPageBean(PageBean pageBean) {
		this.pageBean = pageBean;
	}

	@Override
	public int doStartTag() throws JspException {
		JspWriter out = pageContext.getOut();
		try {
			out.print(toHTML());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return super.doStartTag();
	}

	private String toHTML() {
		StringBuilder sb=new StringBuilder();
		//拼接下一次发送请求所要提交的隐藏的form表单
		sb.append("<form id='pageBeanForm' action='"+pageBean.getUrl()+"' method='post'>");
		//差第几页的数据
		sb.append("<input type='hidden' name='page'>");
		
		Map<String, String[]> pageMap = pageBean.getPageMap();
		if(pageMap.size()>0) {
			Set<Entry<String,String[]>> entrySet = pageMap.entrySet();
			for (Entry<String, String[]> entry : entrySet) {
				if(!"page".equals(entry.getKey())) {
					for(String val:entry.getValue()) {
						sb.append("<input type='hidden' value='"+val+"' name='"+entry.getKey()+"'>");
					}
				}
			}
		}
		sb.append("</form>");
		//默认展示前面四页
		int page =pageBean.getPage();//获取当前页
		int max=pageBean.getMaxPage();//获取最大页
		//当前页前面的页码是否大于4,大于就是4,小于前面页码-1
		int before = page > 4 ? 4 : page-1;
		//总共10页减去当前页,再减去当前页前面的页码
		int after = 10 - 1 - before;
		//最大页-当前页
		after = max-page > after ? after : max-page;
		//用来控制上一页点击按钮特效的
		 boolean startFlag = page > 1;
		 //用来控制下一页点击按钮特效的
	     boolean endFlag = max == page;	

//	        拼接分页条
	        sb.append("<ul class='pagination'>");
	        sb.append("<li class='page-item "+(startFlag ? "disabled" : "")+"'><a class='page-link' href='javascript:gotoPage(1)'>首页</a></li>");
	        sb.append("<li class='page-item "+(startFlag ? "disabled" : "")+"'><a class='page-link' href='javascript:gotoPage("+pageBean.getPrevPage()+")'>&lt;</a></li>");

//	        代表了当前页的前4页
	        for (int i = before; i > 0 ; i--) {
	            sb.append("<li class='page-item'><a class='page-link' href='javascript:gotoPage("+(page-i)+")'>"+(page-i)+"</a></li>");
	        }

	        sb.append("<li class='page-item active'><a class='page-link' href='javascript:gotoPage("+pageBean.getPage()+")'>"+pageBean.getPage()+"</a></li>");

//	        代表了当前页的后5页
	        for (int i = 1; i <= after; i++) {
	            sb.append("<li class='page-item'><a class='page-link' href='javascript:gotoPage("+(page+i)+")'>"+(page+i)+"</a></li>");
	        }

	        sb.append("<li class='page-item "+(endFlag ? "disabled" : "")+"'><a class='page-link' href='javascript:gotoPage("+pageBean.getNextPage()+")'>&gt;</a></li>");
	        sb.append("<li class='page-item "+(endFlag ? "disabled" : "")+"'><a class='page-link' href='javascript:gotoPage("+pageBean.getMaxPage()+")'>尾页</a></li>");
	        sb.append("<li class='page-item go-input'><b>到第</b><input class='page-link' type='text' id='skipPage' name='' /><b>页</b></li>");
	        sb.append("<li class='page-item go'><a class='page-link' href='javascript:skipPage()'>确定</a></li>");
	        sb.append("<li class='page-item'><b>共"+pageBean.getTotal()+"条</b></li>");
	        sb.append("</ul>");

//	        拼接分页的js代码
	        sb.append("<script type='text/javascript'>");
	        sb.append("function gotoPage(page) {");
	        sb.append("document.getElementById('pageBeanForm').page.value = page;");
	        sb.append("document.getElementById('pageBeanForm').submit();");
	        sb.append("}");
	        sb.append("function skipPage() {");
	        sb.append("var page = document.getElementById('skipPage').value;");
	        sb.append("if (!page || isNaN(page) || parseInt(page) < 1 || parseInt(page) > "+max+") {");
	        sb.append("alert('请输入1~N的数字');");
	        sb.append("return;");
	        sb.append("}");
	        sb.append("gotoPage(page);");
	        sb.append("}");
	        sb.append("</script>");

	        return sb.toString();
	}
}

实现规则:总共显示10个页码,当前页码前4个页码后5页码,再就是是否到达一个最大页最小页临界点禁用

int page =pageBean.getPage();//获取当前页
		int max=pageBean.getMaxPage();//获取最大页
		//当前页前面的页码是否大于4,大于就是4,小于前面页码-1
		int before = page > 4 ? 4 : page-1;
		//总共10页减去当前页,再减去当前页前面的页码
		int after = 10 - 1 - before;
		//最大页减当前页
		after = max-page > after ? after : max-page;
		//用来控制上一页点击按钮特效的
		 boolean startFlag = page > 1;
		 //用来控制下一页点击按钮特效的
	     boolean endFlag = max == page;	

拼接完后就是写tld描述文件,写完后在jsp界面进行引用就行了

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@taglib prefix="l" uri="/liuchunming"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.js"></script>
<style type="text/css">
	.page-item input {
	padding: 0;
	width: 40px;
	height: 100%;
	text-align: center;
	margin: 0 6px;
}

.page-item input,
.page-item b {
	line-height: 38px;
	float: left;
	font-weight: 400;
}

.page-item.go-input {
	margin: 0 10px;
}
	
</style>
</head>
<body>
	<form class="form-inline" action="${pageContext.request.contextPath}/book.action">
	  <div class="form-group">
	    <div class="input-group">
	      <input type="text" class="form-control"  name="bname" placeholder="请输入书籍名称">
	    </div>
	  </div>
	  <button type="submit" class="btn btn-primary">搜索</button>
	</form>
	<table class="table table-striped table table-bordered">
		<tr>
		  <td class="active">书籍ID</td>
		  <td class="success">书籍名称</td>
		  <td class="warning">书籍价格</td>
		</tr>
	<c:forEach items="${bookList }" var="i">
		<tr>
		  <td >${i.bid }</td>
		  <td >${i.bname }</td>
		  <td >${i.price }</td>
		</tr>
	</c:forEach>
	</table>
      <l:page pageBean="${pageBean }"></l:page>
</body>
</html>

这里是用到是bootstap在线的样式库,需要联网才能使用

<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.js"></script>

感兴趣的朋友可以去官网瞧瞧Boostrap中文网
在这里插入图片描述
jsp的样式也是在boostrap里找到
action处理层

package com.liuchunming.web;

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.liuchunming.dao.BookDao;
import com.liuchunming.entity.Book;
import com.liuchunming.util.PageBean;

@WebServlet(name="book",urlPatterns="/book.action" )
public class BookAction extends HttpServlet{

	private static final long serialVersionUID = 1L;
	private BookDao bookDao =new BookDao();
	@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 {
		Book book=new Book();
		book.setBname(req.getParameter("bname"));
		PageBean pageBean =new PageBean();
		pageBean.setRequest(req);
		try {
			List<Book> list = bookDao.list(book, pageBean);
			req.setAttribute("bookList", list);
			req.setAttribute("pageBean", pageBean);
			//resp.sendRedirect("bookList.jsp");
			req.getRequestDispatcher("bookList.jsp").forward(req, resp);
		} catch (InstantiationException | IllegalAccessException | SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

效果图

在这里插入图片描述

总结

今天就到这里了如果有什么不对的地方欢迎大家在评论区留言交流改进!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值