如果一张表的数据有非常多,页面就需要分页展示,但一次性从数据库中读取全部的数据会给服务器带来巨大压力。jsp分页技术的设计思想来源于“select * from employee limit ?,?”这条sql语句,第一个“?”指查询的起始位置,第二个“?”代表偏移量。页面需要展示多少数据,每次就从服务器读取多少数据,大大减轻了服务器的压力。下面开始实现一个javaweb的demo帮助大家更好的理解。
一.准备工作:
MySQL中创建一张用例表
CREATE TABLE employee( empId INT PRIMARY KEY AUTO_INCREMENT, empName VARCHAR(20), dept_id VARCHAR(20) );
往里面插入足够的数据(20条就够了)
二.demo采用mvc模式,首先编写JSP页面
表格代码如下
<table border="1" width="80%" align="center" cellpadding="5" cellspacing="0"> <tr> <td>序号</td> <td>员工编号</td> <td>员工姓名</td> </tr> <!-- 迭代数据 --> <c:choose> <c:when test="${not empty requestScope.pageBean.pageData}"> <c:forEach var="emp" items="${requestScope.pageBean.pageData}" varStatus="vs"> <tr> <td>${vs.count }</td> <td>${emp.empId }</td> <td>${emp.empName }</td> </tr> </c:forEach> </c:when> <c:otherwise> <tr> <td colspan="3">对不起,没有你要找的数据</td> </tr> </c:otherwise> </c:choose> <tr> <td colspan="3" align="center"> 当前${requestScope.pageBean.currentPage }/${requestScope.pageBean.totalPage }页 <a href="${pageContext.request.contextPath }/index?currentPage=1">首页</a> <a href="${pageContext.request.contextPath }/index?currentPage=${requestScope.pageBean.currentPage-1}">上一页 </a> <a href="${pageContext.request.contextPath }/index?currentPage=${requestScope.pageBean.currentPage+1}">下一页 </a> <a href="${pageContext.request.contextPath }/index?currentPage=${requestScope.pageBean.totalPage}">末页</a> </td> </tr> </table>
三.表对应实体类的设计,分页的几个重要参数也封装到一个实体类里
package cn.cracker.entity; public class Employee { private int empId; // 员工id private String empName; // 员工名称 private int dept_id; // 部门id public int getEmpId() { return empId; } public void setEmpId(int empId) { this.empId = empId; } public String getEmpName() { return empName; } public void setEmpName(String empName) { this.empName = empName; } public int getDept_id() { return dept_id; } public void setDept_id(int deptId) { dept_id = deptId; } }
package cn.cracker.utils; import java.util.List; import cn.cracker.entity.Employee; public class PageBean<T> { private int currentPage = 1; // 当前页, 默认显示第一页 private int pageCount = 4; // 每页显示的行数(查询返回的行数), 默认每页显示4行 private int totalCount; // 总记录数 private int totalPage; // 总页数 = 总记录数 / 每页显示的行数 (+ 1) private List<T> pageData; // 分页查询到的数据 // 返回总页数 public int getTotalPage() { if (totalCount % pageCount == 0) { totalPage = totalCount / pageCount; } else { totalPage = totalCount / pageCount + 1; } return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public int getCurrentPage() { return currentPage; } public void setCurrentPage(int currentPage) { this.currentPage = currentPage; } public int getPageCount() { return pageCount; } public void setPageCount(int pageCount) { this.pageCount = pageCount; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public List<T> getPageData() { return pageData; } public void setPageData(List<T> pageData) { this.pageData = pageData; } }
四.DbUtils和C3p0配置文件
package cn.cracker.utils; import javax.sql.DataSource; import org.apache.commons.dbutils.QueryRunner; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 工具类 */ public class JdbcUtils { /** * 1. 初始化C3P0连接池 */ private static DataSource dataSource; static { dataSource = new ComboPooledDataSource(); } /** * 2. 创建DbUtils核心工具类对象 */ public static QueryRunner getQueryRuner(){ return new QueryRunner(dataSource); } }
<c3p0-config> <default-config> <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_demo </property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <property name="initialPoolSize">3</property> <property name="maxPoolSize">6</property> <property name="maxIdleTime">1000</property> </default-config> <named-config name="oracle_config"> <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_demo</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <property name="initialPoolSize">3</property> <property name="maxPoolSize">6</property> <property name="maxIdleTime">1000</property> </named-config> </c3p0-config>
五.数据库访问层的设计(包含接口与实现)
实现查询分页的数据和总数据两个功能
package cn.cracker.dao; import cn.cracker.entity.Employee; import cn.cracker.utils.PageBean; public interface IEmployeeDao { public void getAll(PageBean<Employee> pb); public int getTotalCount(); }
package cn.cracker.dao.impl; import java.sql.SQLException; import java.util.List; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import cn.cracker.dao.IEmployeeDao; import cn.cracker.entity.Employee; import cn.cracker.utils.JdbcUtils; import cn.cracker.utils.PageBean; /** * 2. 数据访问层实现 */ public class EmployeeDao implements IEmployeeDao { @Override public void getAll(PageBean<Employee> pb) { //2. 查询总记录数; 设置到pb对象中 int totalCount = this.getTotalCount(); pb.setTotalCount(totalCount); /* * 问题: jsp页面,如果当前页为首页,再点击上一页报错! * 如果当前页为末页,再点下一页显示有问题! * 解决: * 1. 如果当前页 <= 0; 当前页设置当前页为1; * 2. 如果当前页 > 最大页数; 当前页设置为最大页数 */ // 判断 if (pb.getCurrentPage() <=0) { pb.setCurrentPage(1); // 把当前页设置为1 } else if (pb.getCurrentPage() > pb.getTotalPage()){ pb.setCurrentPage(pb.getTotalPage()); // 把当前页设置为最大页数 } //1. 获取当前页: 计算查询的起始行、返回的行数 int currentPage = pb.getCurrentPage(); int index = (currentPage -1 ) * pb.getPageCount(); // 查询的起始行 int count = pb.getPageCount(); // 查询返回的行数 //3. 分页查询数据; 把查询到的数据设置到pb对象中 String sql = "select * from employee limit ?,?"; try { // 得到Queryrunner对象 QueryRunner qr = JdbcUtils.getQueryRuner(); // 根据当前页,查询当前页数据(一页数据) List<Employee> pageData = qr.query(sql, new BeanListHandler<Employee>(Employee.class), index, count); // 设置到pb对象中 pb.setPageData(pageData); } catch (Exception e) { throw new RuntimeException(e); } } @Override public int getTotalCount() { String sql = "select count(*) from employee"; try { // 创建QueryRunner对象 QueryRunner qr = JdbcUtils.getQueryRuner(); // 执行查询, 返回结果的第一行的第一列 Long count = qr.query(sql, new ScalarHandler<Long>()); return count.intValue(); } catch (Exception e) { throw new RuntimeException(e); } } }
六.业务层
package cn.cracker.service; import cn.cracker.entity.Employee; import cn.cracker.utils.PageBean; public interface IEmployeeService { /** * 分页查询数据 */ public void getAll(PageBean<Employee> pb); }
package cn.cracker.service.impl; import cn.cracker.dao.IEmployeeDao; import cn.cracker.dao.impl.EmployeeDao; import cn.cracker.entity.Employee; import cn.cracker.service.IEmployeeService; import cn.cracker.utils.PageBean; /** * 3. 业务逻辑层,实现 */ public class EmployeeService implements IEmployeeService { // 创建Dao实例 private IEmployeeDao employeeDao = new EmployeeDao(); @Override public void getAll(PageBean<Employee> pb) { try { employeeDao.getAll(pb); } catch (Exception e) { throw new RuntimeException(e); } } }
七.控制层
package cn.cracker.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.cracker.entity.Employee; import cn.cracker.service.IEmployeeService; import cn.cracker.service.impl.EmployeeService; import cn.cracker.utils.PageBean; /** * 4. 控制层开发 */ public class IndexServlet extends HttpServlet { // 创建Service实例 private IEmployeeService employeeService = new EmployeeService(); // 跳转资源 private String uri; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { //1. 获取“当前页”参数; (第一次访问当前页为null) String currPage = request.getParameter("currentPage"); // 判断 if (currPage == null || "".equals(currPage.trim())){ currPage = "1"; // 第一次访问,设置当前页为1; } // 转换 int currentPage = Integer.parseInt(currPage); //2. 创建PageBean对象,设置当前页参数; 传入service方法参数 PageBean<Employee> pageBean = new PageBean<Employee>(); pageBean.setCurrentPage(currentPage); //3. 调用service employeeService.getAll(pageBean); // 【pageBean已经被dao填充了数据】 //4. 保存pageBean对象,到request域中 request.setAttribute("pageBean", pageBean); //5. 跳转 uri = "/WEB-INF/list.jsp"; } catch (Exception e) { e.printStackTrace(); // 测试使用 // 出现错误,跳转到错误页面;给用户友好提示 uri = "/error/error.jsp"; } request.getRequestDispatcher(uri).forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
PS:别忘了导入这几个lib包 c3p0-0.9.1.2.jar、commons-dbutils-1.6.jar、mysql-connector-java-5.1.12-bin.jar