【Servlet】利用JSTL表达式,C标签的循环与判断,对数据库查询内容进行分页
分页技术在很多地方都有使用,几乎在超过30条数据需要显示页面,你不得不利用get方法对其进行分页,总不能一次性地在一个页面呈现给用户,而在JavaWeb中实现分页一点也不难,关键是处理好几个关键参数,下面举一个例子说明这个问题。
一、基本目标
在数据库中有张已经被我用烂的Testtable表。
这张表有17条记录,自增的ID是断裂的,也就是说不是按照1、2、3、4……排列下去的,中间有几个数字是断裂的。
假设要把这张表呈现到allData.jsp这个页面,如下图,利用get参数传递的方式,就一个allData.jsp页面就完成每页显示1,2,3,5,10,30条记录。
如果不够30条记录则全部显示。
而且这个表格利用了一个简单的判断黑白相间地显示。
分页部分,显示当前的页数,总页数。当前页没有超级链接。
如果页数太多,则只显示当前页的前1页与后1页,首页与末页。隐藏其它的页数。
如果页数不足7页,则显示全部的页数。
二、基本思想
目录结构如下:
首先在你的JavaWeb工程下的lib文件夹配好相应的支持包。这些包我在以前的文章都讲过了。
Servlet:《【Servlet】最简单的Servlet JavaWeb程序》(点击打开链接)
Mysql:《【Mysql】Java中对Mysql数据库的增删改查、Java的System类》(点击打开链接)
C标签:《【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示》(点击打开链接)
都是Javaweb中很简单的知识。
整个工程,一直都是allData.jsp与getAllData.java在不停地传递参数。dbDAO.java与Testtable.java只是getAllData.java调用来查询数据库的数据、存放数据用的。
allData.jsp向getAllData.java传递两个参数,一个cpage表示当前页,一个per表示每页显示的数据数量。
getAllData.java由于Testtable中id是断裂的,我们应先把所有整个Testtable的数据取出来,再根据cpage与per两个参数,同时配合数据库的含有的数据条数total这个参数,也就是三个参数。整合出一个存放TestTable类的ArrayList<TestTable>、与总页数totalPage给前台的allData.jsp。如果前台没有传递过来cpage与per,那么初定cpage=0,per=5,显示第一页的数据,每页显示5条数据。
总页数totalPage=((数据库中查询结果的总条数total-1)÷每页显示的数据per)+1
要显示查询结果中,从第(当前页cpage×每页显示的数据数量per 条)至第( {当前页cpage×每页显示的数据数量per+每页显示的数据数量per、数据库中查询结果的总条数total}两者之间值少者)的条数。
有了上面的思想,则得到以下的程序。
三、制作过程
1、基本准备
(1)首先web.xml通过Eclipse for JavaEE自动生成,因为用到支持注释的Servlet3.0,因此里面什么都没有。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> </web-app>(2)其次是在《【Servlet】根据MVC思想设计用户登陆、用户注册、修改密码系统》(点击打开链接)中已经讲过的dbDAO.java。
dbDAO.java将给getAllData.java引用,用作数据库查询。
package cPaging; import java.sql.*; public class dbDAO { private Connection con; // 构造函数,连接数据库 public dbDAO() throws Exception { String dburl = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useOldAliasMetadataBehavior=true"; String dbusername = "pc"; String dbpassword = "admin"; Class.forName("com.mysql.jdbc.Driver"); this.con = DriverManager.getConnection(dburl, dbusername, dbpassword); } // 执行查询 public ResultSet query(String sql, Object... args) throws Exception { PreparedStatement ps = con.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } return ps.executeQuery(); } // 执行插入 public boolean insert(String sql, Object... args) throws Exception { PreparedStatement ps = con.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } if (ps.executeUpdate() != 1) { return false; } return true; } // 执行修改 public boolean modify(String sql, Object... args) throws Exception { PreparedStatement ps = con.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } if (ps.executeUpdate() != 1) { return false; } return true; } // 析构函数,中断数据库的连接 protected void finalize() throws Exception { if (!con.isClosed() || con != null) { con.close(); } } }(3)之后如同《【Servlet】利用JSTL表达式把Servlet从数据库读出的数据推向前台与javax.el.PropertyNotFoundException的错误》(点击打开链接)一样,给Mysql数据库这张TestTable表,建立持久化类TestTable.java。同样是给getAllData.java引用,用来存储数据的。
package cPaging; public class TestTable { private int id; private String username; private String number; private String date; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } }注意这个持久化类TestTable.java必须写成上面这种一堆私有private类成员变量+一堆共有的getter、setter方法的方式,因为一个c标签,就是根据这堆getter与setter找变量。我也想定义四个共有的public变量完事,结果c标签因为没有getter与setter而找不到变量。这里其实已经可以理解为何尼玛的struts2也同样以这种方式传递变量了,因为它与Servlet本来就是一伙的。
2、做好上述的基本准备记下来才是真正的主角,allData.jsp与getAllData.java
(1)首先是后台处理方法getAllData.java,也就是通过注释被定义了地址是/getAllData,名为getAllData的Servlet
package cPaging; import java.util.*; import java.sql.*; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; @WebServlet(name = "getAllData", urlPatterns = { "/getAllData" }) public class getAllData extends HttpServlet { //为了eclipse不警告而写的序列号,没什么卵用 private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException { // 如果没有参数传递过来,初定每页显示5条记录,显示第一页。 // 然后把这两个参数压到request容器传到前台页面。request容器是前台页面收到即销毁的容器。 int cpage = 0; int per = 5; if (request.getParameter("cpage") != null) { cpage = Integer.parseInt(request.getParameter("cpage")); } request.setAttribute("cpage", cpage); if (request.getParameter("per") != null) { per = Integer.parseInt(request.getParameter("per")); } request.setAttribute("per", per); // 之后进行数据库的查询 try { // 先数据库的查询结果到底有多少条记录 dbDAO db = new dbDAO(); ResultSet rsTotal = db .query("select count(*) as total from testtable"); if (rsTotal.next()) { // 求出总页数压到request容器传递给前台页面。 request.setAttribute("totalPage", 1 + (rsTotal.getInt("total") - 1) / per); } // 新建一个testtableAllList动态数组用来存放查询结果 ArrayList<TestTable> testtableAllList = new ArrayList<TestTable>(); ResultSet rs = db.query("select * from testtable"); // 先把所有查询结果放到这个testtableAllList int total = 0; while (rs.next()) { TestTable testtable = new TestTable(); testtable.setId(rs.getInt("id")); testtable.setUsername(rs.getString("username")); testtable.setNumber(rs.getString("number")); testtable.setDate(rs.getString("date")); testtableAllList.add(testtable); total++; } // 再通过根据cpage与per求出要推回给前台显示的数组testtableList。 // 并把testtableList压到request容器,同时重定向到allData.jsp这个页面 ArrayList<TestTable> testtableList = new ArrayList<TestTable>(); for (int i = cpage * per; i < cpage * per + per && i < total; i++) { testtableList.add(testtableAllList.get(i)); } request.setAttribute("testtableList", testtableList); request.getRequestDispatcher("/allData.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); } } }(2)allData.jsp取出getAllData.java放在request容器的的cpage、per、testtableList、totalPage则做如下的操作:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%--声明我要使用C标签--%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!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>C标签分页</title> </head> <body> <%--表的输出--%> <%--由于testtableList是后台的getAllData.java方法已经处理好的,直接通过c标签的forEach输出就可以了--%> <table border="1"> <%--判断这个forEach循环的迭代器也就是游标,就是那个int i,这里是status,通过varStatus属性定义就行了无须再设置多一个参数--%> <%--requestScope代表从request容器中取,不这样搞,取不到相应的变量--%> <c:forEach var="testtable" items="${requestScope.testtableList}" varStatus="status"> <%--判断迭代器的单双数以一下的方式去判断,如果是单数则把表格当前的颜色弄成灰色--%> <c:if test="${status.count%2==0}"> <tr> </c:if> <c:if test="${status.count%2==1}"> <tr bgcolor="#cccccc"> </c:if> <td>${testtable.id}</td> <td>${testtable.username}</td> <td>${testtable.number}</td> <td>${testtable.date}</td> <tr/> </c:forEach> </table> <%--分页的输出,如果多于1页才输出这个分页组件,否则只有一页,没这个输出的必要了--%> <c:if test="${requestScope.totalPage>1}"> [ <%--定义计算...的变量count_points,当发现已经输出3个.则没必要继续输出...了--%> <c:set var="count_points" value="0" /> <%--循环从第一页一直到总页数--%> <c:forEach var="page" begin="1" end="${requestScope.totalPage}"> <c:choose> <%--如果少于7页就不要搞这么多东西了,直接输出全部页面--%> <c:when test="${requestScope.totalPage<7}"> <c:choose> <%--当前页不给予超级链接--%> <%--后台Java方法的处理是从第0页开始,人的思维是从第1页开始,这里的page从1开始循环,因此当前页的判断是cpage+1=page--%> <c:when test="${requestScope.cpage+1==page}">${requestScope.cpage+1}</c:when> <c:otherwise> <%--其他页赋予相应的链接,通过get方法,给getAllData生成相应的testtableList输出--%> <a href="./getAllData?cpage=${page-1}&per=${requestScope.per}">${page}</a> </c:otherwise> </c:choose> </c:when> <c:otherwise> <c:choose> <%--超过7页的话,当前页的前1页、后1页、首页、末页正常输出--%> <c:when test="${(page<(requestScope.cpage+1)+2) and (page>(requestScope.cpage+1)-2) or (page==1) or (page==totalPage)}"> <c:choose> <c:when test="${requestScope.cpage+1==page}">${requestScope.cpage+1}</c:when> <c:otherwise> <a href="./getAllData?cpage=${page-1}&per=${requestScope.per}">${page}</a> </c:otherwise> </c:choose> <c:set var="count_points" value="0" /> </c:when> <c:otherwise> <c:if test="${count_points<3}"> <%--否则,则输出点.,如果输出的点还有没超过3个话--%> <c:set var="count_points" value="${count_points+1}" />. </c:if> </c:otherwise> </c:choose> </c:otherwise> </c:choose> </c:forEach> ]第${requestScope.cpage+1}/${requestScope.totalPage}页 </c:if> 每页 <a href="./getAllData?cpage=0&per=1">1</a> <a href="./getAllData?cpage=0&per=2">2</a> <a href="./getAllData?cpage=0&per=3">3</a> <a href="./getAllData?cpage=0&per=5">5</a> <a href="./getAllData?cpage=0&per=10">10</a> <a href="./getAllData?cpage=0&per=30">30</a> 条记录 </body> </html>
这里涉及一个计算点点点数量的变量count_points,这里的思想与《【JavaScript】一个简单的分页,显示页首,中间页,页尾,当前页的前后三页,省略其它页》(点击打开链接)一模一样的。不再赘述了。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。