通用分页

思维导图

通用分页_第1张图片

 

一、通用后台分页

 所用jar包

通用分页_第2张图片

 

类结构:

通用分页_第3张图片

1.反射通用后台查询方法

(1)、普通的方法 

代码如下:

package com.ysq.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.ysq.entity.Book;
import com.ysq.util.DBAccess;
import com.ysq.util.PageBean;
import com.ysq.util.StringUtils;

public class BookDao extends BaseDao{
	/**
	 * 简单的查询方法
	 * 思路:
	 * 	1.建立数据库连接
	 * 	2.预定义对象PrepareStatement
	 * 	3.查询结果集
	 * 	4.处理结果集
	 */

普通的
	public List list(Book book)throws Exception{
		List books=new ArrayList();
		String sql="select * from t_mvc_book where 1=1";
		//书籍名称
		String bname=book.getBname();
		//书籍名称模糊查询
		if(StringUtils.isNotBlank(bname)) {
			sql+=" and bname like '%"+bname+"%'";
		}
		Connection con = DBAccess.getConnection();
		PreparedStatement ps = con.prepareStatement(sql);
		ResultSet rs=ps.executeQuery();
		while(rs.next()) {
			books.add(new Book(rs.getInt("bid"),rs.getString("bname"),rs.getFloat("price")));
		}
		return books;
	}
	
	}
	

缺点总结:需要写大量重复代码,系统中的查询功能越来越多,重复代码量变大

解决方案:

①、抽取变化部分作为参数:sql、Class

②、抽取公共的父类,把重复的代码剥离出来

BaseDao是所有dao层的父类 

package com.ysq.dao;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.ysq.util.DBAccess;
import com.ysq.util.PageBean;


/**
 * 所有Dao层的父类
 * @author T440s
 *
 * @param 
 */
public class BaseDao {
	public List list(String sql,Class clz)throws Exception{
		//不确定list集合中装的什么东西 子类Dao继承BaseDao,子类传的是什么类那么list集合中放的就是什么
		List list=new ArrayList();
		Connection con = DBAccess.getConnection();
		PreparedStatement ps = con.prepareStatement(sql);
		ResultSet rs=ps.executeQuery();
		while(rs.next()) {
			//books.add(new Book(rs.getInt("bid"),rs.getString("bname"),rs.getFloat("price")));
			/**
			 * 上面一行代码分析:
			 * 1.实例化了一个对象
			 * 2.从ResultSet的对象的属性获取值(bid)获取值,赋值给了前面的实例化的对象
			 * 3.把已经赋值了的对象添加到List集合中
			 */
			//实例化一个对象
			T t=clz.newInstance();
			//获取属性
			Field[] fields = clz.getDeclaredFields();
			for (Field f : fields) {
				f.setAccessible(true);
				f.set(t, rs.getObject(f.getName()));
			}
			list.add(t);
		}
		return list;
	}

(2)、继承父类的方法(代码量明显减少)

代码如下:

package com.ysq.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.ysq.entity.Book;
import com.ysq.util.DBAccess;
import com.ysq.util.PageBean;
import com.ysq.util.StringUtils;

public class BookDao extends BaseDao{
	
	public List list2(Book book)throws Exception{
		String sql="select * from t_mvc_book where 1=1";
		//书籍名称
		String bname=book.getBname();
		//书籍名称模糊查询
		if(StringUtils.isNotBlank(bname)) {
			sql+=" and bname like '%"+bname+"%'";
		}
		return super.list(sql, Book.class);
	}

}

(3)、测试:

public static void main(String[] args) throws Exception{
		BookDao b=new BookDao();
		Book book=new Book();
		book.setBname("圣墟");
        //List list = bd.list(book);
		//List list=b.list2(book);
		for (Book book2 : list) {
			System.out.println(book2);
		}
	}

运行结果如下:

通用分页_第4张图片

PageBean

1、 page=1        页码        视图层传递过来

2、rows=10         页大小      视图层传递过来

3、total=0       总记录数    后台查出来

4、pagination=true  是否分页    视图层传递过来

package com.ysq.util;

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

import javax.servlet.http.HttpServletRequest;
import com.ysq.util.StringUtils;
/**
 * 分页工具类
 *
 */
public class PageBean {

	private int page = 1;// 页码

	private int rows = 10;// 页大小

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

	private boolean pagination = true;// 是否分页

	private String url;// 保存上一次请求的URL

	private Map paramMap = new HashMap();// 保存上一次请求的参数

	/**
	 * 初始化pagebean ,保存上一次请求的重要参数
	 */
	public void setRequest(HttpServletRequest req) {
		// 1.1需要保存上一次请求的URL
		this.setUrl(req.getRequestURL().toString());
		// 1.2需要保存上一次请求的参数
		this.setParamMap(req.getParameterMap());
		// 1.3需要保存上一次请求的分页设置
		this.setPagination(req.getParameter("pagination"));
		// 1.4需要保存上一次请求的展示条数
		this.setRows(req.getParameter("rows"));
		// 1.5初始化的页码
		this.setPage(req.getParameter("page"));
	}

	public void setPage(String page) {
		// TODO Auto-generated method stub
		if(StringUtils.isNotBlank(page)) {
			this.setPage(Integer.valueOf(page));
		}
	}

	public void setRows(String rows) {
		// TODO Auto-generated method stub
		if(StringUtils.isNotBlank(rows)) {
			this.setRows(Integer.valueOf(rows));
		}
	}

	public void setPagination(String pagination) {
		// TODO Auto-generated method stub
//		只有在前台jsp填写了pagination=false,才代表不分页
		if(StringUtils.isNotBlank(pagination)) {
			this.setPagination(!"false".equals(pagination));
		}
	}

	public void setPagination(boolean pagination) {
		this.pagination = pagination;
	}

	public String getUrl() {
		return url;
	}

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

	public Map getParamMap() {
		return paramMap;
	}

	public void setParamMap(Map paramMap) {
		this.paramMap = paramMap;
	}

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

	/**
	 * 获得起始记录的下标
	 * 
	 * @return
	 */
	public int getStartIndex() {
		return (this.page - 1) * this.rows;
	}

	/**
	 * 最大页
	 * 
	 * @return
	 */
	public int maxPage() {
		return this.total % this.rows == 0 ? this.total / this.rows : this.total / this.rows + 1;
	}

	/**
	 * 下一页
	 */
	public int nextPage() {
		// 如果当前页小于最大页,那就下一页为当前页加一,如果不小于,说明当前页就是最大页,那就无需加一
		return this.page < this.maxPage() ? this.page + 1 : this.page;
	}

	/**
	 * 上一页
	 * 
	 * @return
	 */
	public int previousPage() {
		return this.page > 1 ? this.page - 1 : this.page;
	}

	@Override
	public String toString() {
		return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pagination=" + pagination + "]";
	}

}

 2、通用的分页后台查询方法

代码如下:

BookDao:

package com.ysq.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.ysq.entity.Book;
import com.ysq.util.DBAccess;
import com.ysq.util.PageBean;
import com.ysq.util.StringUtils;

public class BookDao extends BaseDao{
	
	/**
	 * 思考
	 * 在项目中大量的出现分页的需求
	 * 目的:
	 * 想要做一个通用的分页查询,子类继承父类,自带了分页功能,并且代码量较少
	 * 最终当碰到分页的需求的时候,要写少量代码
	 * 实现:
	 * 	1.MySQL如何分页
	 * 		分页通常有关键的因素:第page页、显示数目(rows)、符合查询条件的总记录数(total)
	 * 		-- sql=select * from t_mvc_book where bname like '%圣墟%' 
			-- pagesql=sql limit (page-1)*rows,rows
			-- countSql=select count(1)from(sql)t
			2.编码思路
				2.1原来list集合中返回的是符合条件的所有数据,现在我需要返回第page页的数据,那么意味着SQL要加工成pageSQL
				2.2需要分页,那么需要取出符合条件的总记录数,保存到pagebean中
					要得出共N页,那么必须求出总记录数:
				 	共N页的算法:total % rows==0 ? total/rows :total/rows+1
	 */
	
	/**
	 * 测试通用分页查询
	 * @param book
	 * @return
	 * @throws Exception
	 */
	public List list3(Book book,PageBean pageBean)throws Exception{
		String sql="select * from t_mvc_book where 1=1";
		//书籍名称
		String bname=book.getBname();
		//书籍名称模糊查询
		if(StringUtils.isNotBlank(bname)) {
			sql+=" and bname like '%"+bname+"%'";
		}
		return super.executeQuery(sql,Book.class,pageBean);
	}
	
}

BassDao:

package com.ysq.dao;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.ysq.util.DBAccess;
import com.ysq.util.PageBean;


	/**
	 * 通用分页查询
	 * @param sql
	 * @param clz
	 * @return
	 * @throws Exception
	 */
	public List executeQuery(String sql,Class clz, PageBean pageBean)throws Exception{
		//不确定list集合中装的什么东西 子类Dao继承BaseDao,子类传的是什么类那么list集合中放的就是什么
		List list=new ArrayList();
		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(String.valueOf(rs.getObject(1)));
			}
//		-- sql = select * from t_mvc_book where bname like '%圣墟%'
//		-- pageSql= sql limit (n-1)*rows,rows	//对应某一页的数据
//		-- countSql=select count(1) from (sql) t	//符合条件的总记录数
			String pageSQL=getPageSQL(sql,pageBean);//符合条件的某一页数据
			ps = con.prepareStatement(pageSQL);
			rs = ps.executeQuery();
		}else {
//			不分页
			con = DBAccess.getConnection();
			ps = con.prepareStatement(sql);//符合条件的所有数据
			rs=ps.executeQuery();
		}
		while(rs.next()) {
			T t=clz.newInstance();
			Field[] fields = clz.getDeclaredFields();
			for (Field f : fields) {
				f.setAccessible(true);
				f.set(t, rs.getObject(f.getName()));
			}
			list.add(t);
		}
		return list;
	}
	
}

测试:

public static void main(String[] args) throws Exception{
		BookDao b=new BookDao();
		Book book=new Book();
		book.setBname("圣墟");
		//List list=b.list2(book);
		PageBean pageBean=new PageBean();
//		查第2页的数据
		pageBean.setPage(2);
//		项目开发中,下拉框的需求 不分页  模拟从JSP传递不分页的信息pagination=false
//		pageBean.setPagination(false);
		List list=b.list3(book, pageBean);
		for (Book book2 : list) {
			System.out.println(book2);
		}
	}

运行结果如下:

通用分页_第5张图片

二、通用前台分页 

1、目标:

利用标签,无需写任何js,html代码就可以完成分页

2、MySql分页

分页通常有关键因素:第page页、显示数目(rows)、符合查询条件的总记录数(total)

原生sql=select * from t_mvc_book where bname like '%圣墟%'

 pageSql=sql limit (n-1)*rows,rows 

countSql=select count(1) from (sql) t

通用分页_第6张图片

package com.ysq.dao;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.ysq.util.DBAccess;
import com.ysq.util.PageBean;


/**
 * 所有Dao层的父类
 * @author T440s
 *
 * @param 
 */
public class BaseDao {
	
	/**
	 * 通用分页查询
	 * @param sql
	 * @param clz
	 * @return
	 * @throws Exception
	 */
	public List executeQuery(String sql,Class clz, PageBean pageBean)throws Exception{
		//不确定list集合中装的什么东西 子类Dao继承BaseDao,子类传的是什么类那么list集合中放的就是什么
		List list=new ArrayList();
		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(String.valueOf(rs.getObject(1)));
			}
//		-- sql = select * from t_mvc_book where bname like '%圣墟%'
//		-- pageSql= sql limit (n-1)*rows,rows	//对应某一页的数据
//		-- countSql=select count(1) from (sql) t	//符合条件的总记录数
			String pageSQL=getPageSQL(sql,pageBean);//符合条件的某一页数据
			ps = con.prepareStatement(pageSQL);
			rs = ps.executeQuery();
		}else {
//			不分页
			con = DBAccess.getConnection();
			ps = con.prepareStatement(sql);//符合条件的所有数据
			rs=ps.executeQuery();
		}
		while(rs.next()) {
			T t=clz.newInstance();
			Field[] fields = clz.getDeclaredFields();
			for (Field f : fields) {
				f.setAccessible(true);
				f.set(t, rs.getObject(f.getName()));
			}
			list.add(t);
		}
		return list;
	}
	/**
	 * 将原生SQL转换成符合条件的总记录数countSQL
	 * @param sql
	 * @return
	 */
	private String getCountSQL(String sql) {
//		countSql=select count(1)from(sql)t//符合条件的总记录数
		return "select count(1) from("+sql+") t";
	}
	
	/**
	 * 将原生SQL转换成pageSQL
	 * @param sql
	 * @param pageBean
	 * @return
	 */
	private String getPageSQL(String sql,PageBean pageBean) {
//		(this.page - 1) * this.rows;
//		pagesql=sql limit (page-1)*rows,rows
		return sql+" limit "+pageBean.getStartIndex()+","+pageBean.getRows();
	}
}

3、自定义JSP标签(page标签):

PageTag:

package com.ysq.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.ysq.util.PageBean;

public class PageTag extends BodyTagSupport{
	private PageBean pageBean;//包含了所有分页相关的元素
	
	public PageBean getPageBean() {
		return pageBean;
	}

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

	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		//没有标签体,要输出内容
		JspWriter out = pageContext.getOut();
		try {
			out.print(toHTML());
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		return super.doStartTag();
	}

	private String toHTML() {
		StringBuffer sb=new StringBuffer();
		//隐藏的form表单 这个就是上一次请求下次重新发的奥义所在
		sb.append("
"); sb.append(" "); // 上一次请求的参数 Map paramMap=pageBean.getParamMap(); if(paramMap !=null&¶mMap.size()>0) { Set> entrySet = paramMap.entrySet(); for (Entry entry : entrySet) { // 参数名 String key = entry.getKey(); // 参数值 for (String value : entry.getValue()) { // 上一次的请求的参数,再一次组装成了新的form表单 // 注意:page参数每次都会提交 if(!"page".equals(key)) { sb.append(" "); } } } } sb.append(""); //分页条 sb.append("
    "); sb.append("
  • 首页
  • "); sb.append("
  • <
  • "); // sb.append("
  • 1
  • "); // sb.append("
  • 2
  • "); sb.append("
  • "+pageBean.getPage()+"
  • "); sb.append("
  • >
  • "); sb.append("
  • 尾页
  • "); sb.append("
  • 到第
  • "); sb.append("
  • 确定
  • "); sb.append("
  • 共"+pageBean.getTotal()+"条
  • "); sb.append("
"); //分页执行的jsp代码 sb.append(""); return sb.toString(); } }

4、标签库描述文件




    
  ysq 1.1 core library
  ysq core
  1.1
  ysq
  http://jsp.veryedu.cn 
  
    page
    com.ysq.tag.PageTag
    JSP
    
        pageBean
        true
        true
      
  

Jsp页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	<%@taglib uri="http://jsp.veryedu.cn" prefix="y"%>
	<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>






书籍列表



	
书籍ID 书籍名 价格
${b.bid } ${b.bname } ${b.price }

测试:

BookServlet:

package com.ysq.Servlet;

import java.io.IOException;
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.ysq.dao.BookDao;
import com.ysq.entity.Book;
import com.ysq.util.PageBean;
@WebServlet("/book/search")
public class BookServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		/**
		 * 痛点:
		 * 每次做分页需求的时候,都会有大量的重复的后台代码以及前台代码
		 * 	后台代码重复代码已解决
		 * 	前台重复代码问题:
		 * 		1.大量的前台HTML重复
		 * 		2.大量的前台JavaScript代码重复
		 * 目标:
		 * 	定义标签,无需写任何的HTML以及js,就可以完成前端分页
		 * 分析:
		 * 	1.前台分页后一次请求相较于上一次,只不过是页码改变了
		 * 		1.1需要保存上一次请求的URL
		 * 		1.2需要保存上一次请求的参数  bname、price
		 * 		1.3需要保存上一次请求的分页设置  pagination
		 * 		1.4需要保存上一次请求的展示条数
		 * 		1.5初始化的页码
		 * 2.自定义JSP标签(page标签)
		 * 	定义pagebean 因为pagebean中包含了分页中所有的元素(page/rows/pagination/total/nextPage/previousPage/maxPage)
		 * 	
		 * 
		 * 前台JSP传递书名,后台接收
		 */
		BookDao bookDao=new BookDao();
		Book book=new Book();
		book.setBname(req.getParameter("bname"));
		PageBean pageBean=new PageBean();
		pageBean.setRequest(req);
		try {
			List list3 = bookDao.list3(book, pageBean);
			req.setAttribute("books", list3);
			req.setAttribute("pageBean", pageBean);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		req.getRequestDispatcher("/index.jsp").forward(req, resp);
//		作用:接收jsp页面传递到后台的参数值键值对也就说白了把parameterMap遍历可以拿到hoppy以及bname
//		Map parameterMap=req.getParameterMap();
		
	}
}

运行结果如下:

通用分页_第7张图片

 通用分页_第8张图片

 

你可能感兴趣的:(数据库,sql)