《JavaWeb从入门到改行》分页功能的实现
@目录
项目案例: mysql + commons-dbutils+itcast-tools+BaseServlet + 分页+JSP+JSTL+EL+MVC模式
什么是分页?
如上所示,就是分页 ,不用多说了
子模块功能的问题分析 和 解决方案
@总功能分析 常规JDBC中,点击查询或输入条件查询,在页面中可显示查询出的所有记录,有多少记录就显示多少。在这种项目的基础上增加分页功能 。 @分页功能的宗旨 是无论什么样的查询,显示出的记录都是当前页的记录 。 所以,我们必须得向系统说明我们当前是要显示第几页的数据 。 自然,需要一个PageBean类
public class PageBean<T> { private int pageCode ;//当前页码 // private int totalPage ;//总页数,总页数应该由计算获取 private int totalRecord;//总记录数 private int pageSize;//每页记录数,这是参数是定做这个软件的甲方提出来的,在Servlet中直接定义为10了 private List<T> beanList;//当前页的记录集合,方便整页记录的转移 private String url ;//Url后的条件,条件查询设置的 }
这个PageBean类中封装了当前页、总记录数、当前页所有记录的集合等,系统通过当前页码这个参数返回pageSize条记录放在beanList集合中返回显示。
@子模块功能1 中首页的pageCode是1,尾页的pageCode是totalPage,上一页和下一页分别是pageCode-1 和pageCode+1 。
@子模块功能2 先来分析一个功能 : 当前页在页码列表中是第几个位置 ?
从上述两张图片中,点击1页码,当前页处于列表的第1个位置,点击2页码,当前页处于列表的第2个位置,..........,点击6页码,当前页处于列表的第6个位置,但是当点击7页码时,当前页还是处于列表的第6个位置 。 也就是说,假如每次一行只能显示10个页码,1~6页码的当前页的位置还是1~6,列表从页码1~页码10。 但是7~10页码的当前页的位置始终位于页码列表的第6个位置 ,列表从【页码2~页码11】~【页码X~页码totalPage】。其实非常好解决 ,列表头尾的判定 还是利用pageCode , begin = pageCode - 5 , end = pageCode + 4 。
@解决
- 如果 totalPage <= 10(列表长度),那么 begin = 1,end = totalPage ;
- 使用公式计算;begin = pc-5 , end = pc + 4;
- 头溢出:当 begin < 1 时 , 让 begin = 1 ;
- 尾溢出:当 end > totalPage 时 , 让 end = totalPage
有条件查和无条件查询的影响 和 解决方案
@产生一个问题 分页使得有条件查询产生一个严重问题 , 通过条件查询出40条记录,分4页显示,只有第一页是该条件下的记录,其余3页都是不带条件的记录 。所以我们必须告诉系统我们的条件是什么,每一页都要带着条件去查询和返回 。
@解决 在pageBean中设置url参数 , 这个参数将会携带这条件传递到Servlet中 。
项目案例(mvc+jdbc+c3p0+BaseServlet+mysql+JSP+EL+JSTL)
@使用 mysql数据库,c3p0数据库连接池,
采用commons-dbutils组件,封装好的JdbcUtils工具类和TxQueryRunner工具类辅助JDBC , (学习地址 : http://www.cnblogs.com/zyuqiang/p/7218083.html)
BaseServlet辅助类
JSP+EL+JSTL
@数据库
1 CREATE TABLE t_customer ( 2 username VARCHAR(50) DEFAULT NULL, 3 age INT(11) DEFAULT NULL, 4 balance DOUBLE(20,5) DEFAULT NULL 5 );
@源码
1 package cn.kmust.pagination.customer.domain; 2 /** 3 * 实体类 4 * 变量名字与数据库中对应字段名一样,便于封装操作 5 * @author ZHAOYUQIANG 6 * 7 */ 8 public class Customer { 9 private String username ; //用户 10 private int age ; //年龄 11 private double balance ; //资金 12 public String getUsername() { 13 return username; 14 } 15 public void setUsername(String username) { 16 this.username = username; 17 } 18 public int getAge() { 19 return age; 20 } 21 public void setAge(int age) { 22 this.age = age; 23 } 24 public double getBalance() { 25 return balance; 26 } 27 public void setBalance(double balance) { 28 this.balance = balance; 29 } 30 @Override 31 public String toString() { 32 return "Customer [username=" + username + ", age=" + age + ", balance=" 33 + balance + "]"; 34 } 35 public Customer(String username, int age, double balance) { 36 super(); 37 this.username = username; 38 this.age = age; 39 this.balance = balance; 40 } 41 public Customer() { 42 super(); 43 // TODO Auto-generated constructor stub 44 } 45 46 47 48 49 }
1 package cn.kmust.pagination.pageBean.domain; 2 import java.util.List; 3 import java.util.List; 4 /** 5 * 分页Bean 6 * 把每一页中的当前页码、总页数、总记录数、每页记录数、当前页的记录集合等参数 7 * 封装到该类的一个对象中,形成PageBean对象 8 * @author ZHAOYUQIANG 9 * 10 */ 11 public class PageBean<T> { 12 private int pageCode ;//当前页码 13 // private int totalPage ;//总页数,总页数应该由计算获取 14 private int totalRecord;//总记录数 15 private int pageSize;//每页记录数,这是参数是定做这个软件的甲方提出来的,在Servlet中直接定义为10了 16 private List<T> beanList;//当前页的记录集合,方便整页记录的转移 17 private String url ;//Url后的条件,条件查询设置的 18 19 public String getUrl() { 20 return url; 21 } 22 public void setUrl(String url) { 23 this.url = url; 24 } 25 public int getPageCode() { 26 return pageCode; 27 } 28 public void setPageCode(int pageCode) { 29 this.pageCode = pageCode; 30 } 31 /* 32 * 计算总页数,这个页数是由总记录和每页记录数决定的 33 */ 34 public int getTotalPage() { 35 int totalPage = totalRecord/pageSize ; 36 return totalRecord%pageSize==0 ? totalPage : totalPage+1; 37 } 38 public int getTotalRecord() { 39 return totalRecord; 40 } 41 public void setTotalRecord(int totalRecord) { 42 this.totalRecord = totalRecord; 43 } 44 public int getPageSize() { 45 return pageSize; 46 } 47 public void setPageSize(int pageSize) { 48 this.pageSize = pageSize; 49 } 50 public List<T> getBeanList() { 51 return beanList; 52 } 53 public void setBeanList(List<T> beanList) { 54 this.beanList = beanList; 55 } 56 57 }
1 package cn.kmust.pagination.customer.servlet; 2 3 import java.io.IOException; 4 import java.io.UnsupportedEncodingException; 5 6 import javax.servlet.ServletException; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 import cn.itcast.commons.CommonUtils; 11 import cn.itcast.servlet.BaseServlet; 12 import cn.kmust.pagination.customer.domain.Customer; 13 import cn.kmust.pagination.customer.service.CustomerService; 14 import cn.kmust.pagination.pageBean.domain.PageBean; 15 /** 16 * Web层 17 * 继承了我们自己写的BaseServlet类 18 * 要求写的方法得与service()相同, 19 * 并且.jsp页面必须传递过来一个method参数,如(add、deleter等) 20 * 21 * @author ZHAOYUQIANG 22 * 23 */ 24 public class CustomerServlet extends BaseServlet { 25 /* 26 * 依赖CustomerService 27 */ 28 private CustomerService cstmService = new CustomerService(); 29 /** 30 * 客户查询所有 31 * 加入了分页功能 : 向页面返回当前页的所有记录, 返回的是一个对象,对象中封装了一个beanList集合 32 * 该集合中存放这当前页所有记录 33 * @param request 34 * @param response 35 * @return 36 * @throws ServletException 37 * @throws IOException 38 */ 39 public String queryAll(HttpServletRequest request, HttpServletResponse response) 40 throws ServletException, IOException { 41 /* 42 * 1. 获取list.jsp页面传递的pc 43 * 2. 给定每页记录数ps = 10 。开发者根据客户的需求,认为规定的 44 * 3. 使用pc和ps调用sevice方法,得到当前页的PageBean对象(该对象中封装了 45 * 当前页的所有记录的集合等) 46 * 4. 把PageBean保存到request域中 47 * 5. 转发到list.jsp 48 * 49 */ 50 int pageCode = getCurrentPage(request); 51 int pageSize = 10 ; //每页10记录 52 PageBean<Customer> pageBean = cstmService.queryAll(pageCode,pageSize); 53 /* 54 * 因为与条件查询共用一个list.jsp,所以也需要加url 55 */ 56 pageBean.setUrl(getUrl(request)); 57 request.setAttribute("pageBean", pageBean); 58 return "f:/list.jsp"; 59 60 } 61 62 63 /** 64 * 条件查询 65 * 具有分页功能,按照条件查询,条件也会被带入分页中 66 * @param request 67 * @param response 68 * @return 69 * @throws ServletException 70 * @throws IOException 71 */ 72 public String query(HttpServletRequest request, HttpServletResponse response) 73 throws ServletException, IOException { 74 /* 75 * 1. 封装表单数据到Customer对象中 76 * 只有一个属性username,是查询的条件critaria 77 * 2. 得到当前页码 pageCode 78 * 3. 给定pageSize 10 79 * 4. 使用pageCode和pageSize以及条件对象,调用service方法得到query, 80 * 返回当前页的PageBean对象,内封装了当前页的所有记录集合 81 * 5. 把PageBean保存到request域中 82 * 6. 转发到list.jsp显示成功信息 83 */ 84 Customer criteria = CommonUtils.toBean(request.getParameterMap(), Customer.class); 85 /* 86 * 处理GET请求方式编码问题 87 * 因为query.jsp表单使用的是get提交方法 88 */ 89 criteria = encoding(criteria); 90 91 int pageCode = getCurrentPage(request); 92 int pageSize = 10 ; //每页10记录 93 PageBean<Customer> pageBean = cstmService.query(criteria,pageCode,pageSize); 94 /* 95 * 得到url,保存到pageBean对象中 96 */ 97 pageBean.setUrl(getUrl(request)); 98 request.setAttribute("pageBean", pageBean); 99 return "f:/list.jsp"; 100 } 101 102 103 /** 104 * 处理GET请求乱码 105 * 因为BaseSevlet类中只有处理POST请求的乱码,没有GET请求的乱码处理 106 * @param criteria 107 * @return 108 * @throws UnsupportedEncodingException 109 */ 110 private Customer encoding(Customer criteria) throws UnsupportedEncodingException { 111 String username = criteria.getUsername(); 112 113 if(username != null && !username.trim().isEmpty()){ 114 username = new String(username.getBytes("ISO-8859-1"),"utf-8"); 115 criteria.setUsername(username); 116 } 117 return criteria; 118 } 119 120 121 122 123 /** 124 * 获取pageCode(当前页码) 125 * 传递到Servlet中的参数都是String类型的,所以需要转换 126 * pageCode参数如果不存在,说明是第一次调用,当前页码默认为第一页 127 * @param request 128 * @return 129 */ 130 private int getCurrentPage(HttpServletRequest request){ 131 String value = request.getParameter("pageCode"); 132 if(value == null || value.trim().isEmpty()){ 133 return 1 ; 134 } 135 return Integer.parseInt(value); 136 } 137 138 139 /** 140 * 截取url 141 * 条件查询中需要使用 142 * /项目名/Servlet路径?参数字符串 143 * @param request 144 * @return 145 */ 146 private String getUrl(HttpServletRequest request){ 147 /* 148 * 1. 获取项目名 149 * 2. 获取Servlet路径 150 * 3. 获取参数列表 151 * 这个参数是条件 152 */ 153 String contextPath = request.getContextPath(); 154 String servletPath = request.getServletPath(); 155 String queryString = request.getQueryString(); 156 /* 157 * 4. 判断是否包含pageCode参数 158 * 如果包含需要剪掉,不要这个参数 159 */ 160 if(queryString.contains("&pageCode")){ 161 int index = queryString.lastIndexOf("&pageCode"); 162 queryString = queryString.substring(0,index); 163 } 164 return contextPath+servletPath+"?"+queryString ; 165 } 166 167 }
1 package cn.kmust.pagination.customer.service; 2 3 import java.sql.SQLException; 4 import java.util.List; 5 6 import cn.itcast.jdbc.JdbcUtils; 7 import cn.kmust.pagination.customer.dao.CustomerDao; 8 import cn.kmust.pagination.customer.domain.Customer; 9 import cn.kmust.pagination.pageBean.domain.PageBean; 10 11 12 13 /** 14 * service 层 处理业务 15 * @功能 16 * 1. 条件查询 17 * 2. 查询 所有用户 18 * 3. 查询 所有记录行数 19 * @author ZHAOYUQIANG 20 * 21 */ 22 public class CustomerService { 23 /* 24 * 依赖CustomerDao 25 */ 26 CustomerDao cstmDao = new CustomerDao(); 27 /** 28 * 条件查询 29 * 30 * @param criteria 31 * @param pageCode 32 * @param pageSize 33 * @return 34 */ 35 public PageBean<Customer> query(Customer criteria,int pc,int ps) { 36 return cstmDao.query(criteria,pc,ps); 37 } 38 /** 39 * 查询所有客户业务 40 * 具有分页功能 : 返回PageBean对象 ,对象中封装了 当前页所有记录的集合 41 * @param pc 42 * @param ps 43 * @return 44 */ 45 public PageBean<Customer> queryAll(int pc,int ps){ 46 return cstmDao.queryAll(pc,ps); 47 } 48 49 50 51 }
1 package cn.kmust.pagination.customer.dao; 2 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.ArrayList; 6 import java.util.List; 7 import java.util.Map; 8 9 import javax.sql.DataSource; 10 11 import org.apache.commons.dbutils.QueryRunner; 12 import org.apache.commons.dbutils.ResultSetHandler; 13 import org.apache.commons.dbutils.handlers.BeanHandler; 14 import org.apache.commons.dbutils.handlers.BeanListHandler; 15 import org.apache.commons.dbutils.handlers.MapHandler; 16 import org.apache.commons.dbutils.handlers.MapListHandler; 17 import org.apache.commons.dbutils.handlers.ScalarHandler; 18 19 import cn.itcast.jdbc.JdbcUtils; 20 import cn.itcast.jdbc.TxQueryRunner; 21 import cn.kmust.pagination.customer.domain.Customer; 22 import cn.kmust.pagination.pageBean.domain.PageBean; 23 24 25 26 27 /** 28 *dao层 29 * 对数据库的操作 30 * @author ZHAOYUQIANG 31 * 32 */ 33 public class CustomerDao { 34 private QueryRunner qr = new TxQueryRunner(); 35 /** 36 * 对数据库进行查询所有客户操作 37 * 具有分页功能 : 返回PageBean对象,该对象封装了当前页的所有记录的集合 38 * 当前页的所有记录都放到beanList集合中,然后随当前页码等参数封装到pageBean中返回 39 * @return 40 */ 41 public PageBean<Customer> queryAll(int pc,int ps){ 42 try { 43 /* 44 * 有关数据库的操作: 45 * 准备sql模版 46 * 调用QueryRunner的query方法 47 */ 48 /*该方法的任务: 49 * 1. 创建PageBean对象 50 */ 51 PageBean<Customer> pageBean = new PageBean<Customer>(); 52 /* 53 * 2. 设置pc和ps,封装到我pageBean对象 54 */ 55 pageBean.setPageCode(pc); 56 pageBean.setPageSize(ps); 57 /* 58 * 3. 查询数据库得到totalRecord(数据库所有记录),封装到我pageBean对象 59 */ 60 String sql = "select count(*) from t_customer"; 61 Number num = (Number)qr.query(sql,new ScalarHandler() ); 62 int totalRecord = num.intValue(); 63 pageBean.setTotalRecord(totalRecord); 64 /* 65 * 4. 查询数据库得到BeanList(当前页记录的集合),封装到我pageBean对象 66 * 当前页到底是第几页,当前页有多少条记录呢? 67 * 利用mysql的limit子句, (limit 4,10的意思是从第五行记录开始(包含第五行),查询10行记录) 68 * 很明显当前页的是从第(pc-1)*ps行开始,查询ps行记录。 69 * 根据limit子句的特点,巧妙的设计查询当前页的数据 70 * 用order by 通过客户姓名来排序显示 71 */ 72 sql = "select * from t_customer order by username limit ?,?"; 73 List<Customer> beanList = qr.query(sql, 74 new BeanListHandler<Customer>(Customer.class),(pc-1)*ps,ps); 75 pageBean.setBeanList(beanList); 76 /* 77 * 5. 返回PageBean对象 78 */ 79 return pageBean ; 80 } catch (SQLException e) { 81 throw new RuntimeException(e); 82 } 83 } 84 /** 85 * 条件查询 86 * 具有分页功能 87 * @param criteria 88 * @param pc 89 * @param ps 90 * @return 91 */ 92 public PageBean<Customer> query(Customer criteria,int pc,int ps) { 93 try { 94 /* 95 * 1. 创建PageBean对象 96 * 2. 设置已有属性,pc和ps(pageCode和pageSize) 97 * 3. 通过条件查询数据库得到totalRecord 98 * 4. 查询数据库,返回beanList(当前页记录的集合) 99 */ 100 PageBean<Customer> pageBean = new PageBean<Customer>(); 101 pageBean.setPageCode(pc); 102 pageBean.setPageSize(ps); 103 /* 104 * 3. 通过条件查询数据库得到totalRecord 105 */ 106 String sql1 = null; 107 String username = criteria.getUsername(); 108 if(username != null && !username.trim().isEmpty()){ 109 sql1 = "select count(*) from t_customer where username like ?" ; 110 } 111 Object[] params1 = {"%"+username+"%"}; 112 /* 113 * 3.3. 得到totalRecord 114 */ 115 Number num = (Number)qr.query(sql1, 116 new ScalarHandler(),params1); 117 int totalRecord = num.intValue(); 118 pageBean.setTotalRecord(totalRecord); 119 /* 120 * 4. 查询数据库,返回beanList(当前页记录的集合) 121 * 还是需要拼凑sql语句,并且需要limit子句 122 * params中需要给出limit后两个问号对应的值 123 * 124 */ 125 126 String sql2 = "select * from t_customer where username like ? limit ?,?"; 127 128 Object[] params = {"%"+username+"%",(pc-1)*ps,ps}; 129 List<Customer> beanList = qr.query(sql2, 130 new BeanListHandler<Customer>(Customer.class), 131 params); 132 pageBean.setBeanList(beanList); 133 return pageBean ; 134 } catch (SQLException e) { 135 throw new RuntimeException(e); 136 } 137 138 } 139 }
@项目download https://files.cnblogs.com/files/zyuqiang/jdbcStudy_Demo5_pagination.rar