【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】一个简单的分页,显示页首,中间页,页尾,当前页的前后三页,省略其它页》(点击打开链接)一模一样的。不再赘述了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。