通用分页01

通用分页


1. 什么是标签


2. JSP自定义标签库

2.1 JSP标签库是什么

2.2 处理流程

2.3 如何自定义标签

2.4 标签类型

2.5 开发示例

2.5.1 out标签

2.5.2 if标签

2.5.3 foreach标签

3. 通用分页功能

3.1 总体思路

3.2 分页信息实体(PageBean)

3.3 后台分页数据查询

3.3.1 处理流程

3.3.2 实现

3.3.3 重构-提取公用方法

3.4 分页标签

3.4.1 准备一个Servlet

3.4.2 结果展示页面

3.4.3 过滤器解决中文乱码问题

3.4.3 加入分页功能

3.4.4 封装分页标签

什么是标签

标记语言 是一种注释文本的语言,以方便计算机可以操作。很多与“ML”结尾的语言都是标记语言,比如:HTML,XML,XHTML,VML等等。
标记语言与其他语言一样,也需要运行他们的环境,比如HTML的运行环境是浏览器,XML也需要自己的解析和运行环境

2. JSP自定义标签库
2.1 JSP标签库是什么
是一个JSP标签集合,它封装了JSP应用的通用核心功能, 基于JSP标签我们可以理解为,是JSP应该通用功能的一种封装方式。

2.2 处理流程

2.3 如何自定义标签
常见自定义标签主要步骤:

编写助手类
编写标签库描述文件(tld)
在页面上引入标签使用

2.4 标签类型
UI标签, 输出页面元素
控制标签, 如if标签,foreach标签等
数据标签,用于向页面输入数据

2.5 开发示例

2.5.1 out标签
(1.创建助手类)

public class OutTag extends BodyTagSupport {
	
	private String val;
	
	private String defaultVal;

	public String getVal() {
		return val;
	}

	public void setVal(String val) {
		this.val = val;
	}

	
	public String getDefaultVal() {
		return defaultVal;
	}

	public void setDefaultVal(String defaultVal) {
		this.defaultVal = defaultVal;
	}

	@Override
	public int doStartTag() throws JspException {
		
		//pageContext为页面上下文对象,用于和jsp共享数据
		JspWriter out = this.pageContext.getOut();
		try {
			
			if(this.val == null || "".equals(this.val)) {
				//利用JspWrite对象将val输出到页面
				out.println(this.defaultVal);
			} else {
				//利用JspWrite对象将val输出到页面
				out.println(this.val);
			}
			
		} catch (IOException e) {
			throw new JspException("out标签异常", e);
		}
		
		return SKIP_BODY;
	}

}

(2) 编写tld文件,(必须放在WEB-INF目录或其目录下)

<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<!-- 标签库描述符 -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	<short-name>Simple Tags</short-name>
	<uri>/zking</uri>
	
	<tag>
		<!-- 标签名 -->
		<name>out</name>
		<!-- 标签助手类 -->
		<tag-class>com.zking.mvc.tag.OutTag</tag-class>
		<!-- 标签的内容类型:empty表示空标签,jsp表示可以为任何合法的JSP元素 -->
		<body-content>empty</body-content>
		<attribute>
			<!-- 属性名, OutTag类中的val属性相匹配 -->
			<name>val</name>
			<!-- 表示该属性为必要的属性 -->
			<required>true</required>
			<!-- 该属性可以接受EL表示式的值 -->
			<rtexprvalue>true</rtexprvalue>
			<!-- 标签描述,用于说明标签的作用 -->
			<description>out标签val属性,用于输出val的值</description>
		</attribute>
		
		<attribute>
			<!-- 属性名, OutTag类中的defaultVal属性相匹配 -->
			<name>defaultVal</name>
			<!-- 表示该属性为必要的属性 -->
			<required>false</required>
			<!-- 该属性可以接受EL表示式的值 -->
			<rtexprvalue>false</rtexprvalue>
			<!-- 标签描述,用于说明标签的作用 -->
			<description>out标签,如val属性值为空,则输出该默认值</description>
		</attribute>
	</tag>
	
</taglib>

(3) 在页面上引入使用

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="z" uri="/zking" %>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<h1><%=System.currentTimeMillis()%></h1>
	<z:hello/>
	
	<!-- out -->
	<z:out val="adb"/>
	<%
	  request.setAttribute("name", null);
	%>
	<z:out val="${name}" defaultVal="test"/>
	
</body>
</html>

(4)将项目部署运行,即可可到效果

2.5.2 if标签

1)编写助手类

public class IfTag extends BodyTagSupport {
	
	private boolean test;

	public boolean isTest() {
		return test;
	}

	public void setTest(boolean test) {
		this.test = test;
	}
	
	@Override
	public int doStartTag() {
		
		if(this.test) {
			return EVAL_BODY_INCLUDE;
		}
		
		return SKIP_BODY;
	}
	
}

(2) tld文件,在tld中加入if标签的声明

<tag>
		<name>if</name>
		<tag-class>com.zking.mvc.tag.IfTag</tag-class>
		<!--该标签有标签体-->
		<body-content>jsp</body-content>
		<attribute>
			<name>test</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>

(3) 在页面上使用

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="z" uri="/zking" %>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<h1><%=System.currentTimeMillis()%></h1>
	<z:hello/>
	
	<!-- out -->
	<z:out val="adb"/>
	<%
	  request.setAttribute("name", null);
	%>
	<z:out val="${name}" defaultVal="test"/>
	<p>
	
	<!-- if -->
	<z:if test="${100 == 100}">
		测试if(100 == 100)
	</z:if>
	<z:if test="${100 == 1}">
	        测试if(100 == 1)
	</z:if>
	
</body>
</html>

2.5.3foredch标签

(1)编写助手类

public class ForeachTag extends BodyTagSupport {
	
	//存放数据源
	private List<?> items;
	
	//每次循环获取的对象放入pageContext中,并以var属性的值为key进行保存
	//示例: 页面上标签var属性指定为item,则每次循环取出的对象(obj)将执行如下:
	//pageContext.setAttribute("item", obj); 
	//页面中可以使用EL表达式取出对象中的属性, 如: ${item.name}
	private String var;

	public List<?> getItems() {
		return items;
	}

	public void setItems(List<?> items) {
		this.items = items;
	}

	public String getVar() {
		return var;
	}

	public void setVar(String var) {
		this.var = var;
	}
	
	@Override
	public int doStartTag() {
		
		if(Objects.isNull(this.items) || this.items.size() == 0) {
			return SKIP_BODY;
		}
		
		Iterator<?> iterator = this.items.iterator();
		Object obj = iterator.next();
		this.pageContext.setAttribute(var, obj);
		this.pageContext.setAttribute("iterator", iterator);
		
		return EVAL_BODY_INCLUDE;
	}
	
	
	@Override
	public int doAfterBody() {
		
		Iterator<?> it = (Iterator<?>)this.pageContext.getAttribute("iterator");
		if(it.hasNext()) {
			this.pageContext.setAttribute(var, it.next());
			return EVAL_BODY_AGAIN;
		}
		
		return SKIP_BODY;
	}

}

测试数据辅助类

public class Book {
   private int id;
    private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
    
}
public class TestData {
	
	public static List<Book> getBooks() {
		
		List<Book> books = new ArrayList<>();
		
		Book b1 =  new Book();
		b1.setId(1);
		b1.setName("水浒传");
		Book b2 =  new Book();
		b2.setId(2);
		b2.setName("红楼梦");
		Book b3 =  new Book();
		b3.setId(3);
		b3.setName("西游记");
		
		books.add(b1);
		books.add(b2);
		books.add(b3);
		
		return books;
	}

(3) 在页面上使用标签

<%@page import="com.zking.mvc.test.*,java.util.List" %>
<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="z" uri="/zking" %>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<h1><%=System.currentTimeMillis()%></h1>
	<z:hello/>
	
	<!-- out -->
	<z:out val="adb"/>
	<%
	  request.setAttribute("name", null);
	%>
	<z:out val="${name}" defaultVal="test"/>
	<p>
	
	<!-- if -->
	<z:if test="${100 == 100}">
		测试if(100 == 100)
	</z:if>
	<z:if test="${100 == 1}">
	        测试if(100 == 1)
	</z:if>
	
	<p>
	<!-- foreach -->
	<%
	//获取测试数据
	List<Book> books = TestData.getBooks();
	//放入request对象中
	request.setAttribute("books", books);
	%>
	<z:foreach items="${books}" var="book">
		<p>${book.id } - ${book.name }</p>
	</z:foreach>
	
</body>
</html>

3. 通用分页功能

3.1 总体思路
(1) 为了提高查询性能及节约网络流量,每次只查询指定的记录数,而不是全部,在数量比较大时很有用
(2)当点击下一页或指定页面的数据时,将带着所有的查询条件,再次执行查询

3.2 分页信息实体(PageBean)
用于存储和传递分页参数,主要内容如下:

1.页码,从页面传递过来
2.每页行数,从也能传递过来
3.总记录数, 从数据库中统计得到
4.是否分页, 如果为false,则查询所有记录
5.查询参数, 点击上一页或下一页时需要及携带用户输入的所有查询参数
另外提供上页,下页,总页数等计算

public class PageBean {
	
	/**
	 * 页码
	 */
	private int page = 1;

	/**
	 * 每页显示的记录数
	 */
	private int rows = 10;

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

	/**
	 * 是否分页
	 */
	private boolean pagination = true;
	
	/**
	 * 记录查询的url,以便于点击分页时再次使用
	 */
	private String url;
	
	/**
	 * 存放请求参数,用于生成隐藏域中的元素
	 */
	private Map<String,String[]> parameterMap;
	
	/**
	 * 根据传入的Request初始化分页对象
	 * @param request
	 */
	public void setRequest(HttpServletRequest request) {
		
		if(!StringUtils.isNullOrEmpty(request.getParameter("page"))) {
			this.page = Integer.valueOf(request.getParameter("page"));
		}
		if(!StringUtils.isNullOrEmpty(request.getParameter("rows"))) {
			this.rows = Integer.valueOf(request.getParameter("rows"));
		}
		if(!StringUtils.isNullOrEmpty(request.getParameter("pagination"))) {
			this.pagination = Boolean.valueOf(request.getParameter("pagination"));
		}
		
		this.url = request.getRequestURI();
		this.parameterMap = request.getParameterMap();
		
		request.setAttribute("pageBean", this);
	}


	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 boolean isPagination() {
		return pagination;
	}

	public void setPagination(boolean pagination) {
		this.pagination = pagination;
	}
	
	public String getUrl() {
		return url;
	}

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

	public Map<String, String[]> getParameterMap() {
		return parameterMap;
	}

	public void setParameterMap(Map<String, String[]> parameterMap) {
		this.parameterMap = parameterMap;
	}

	//计算起始页码
	public int getStartIndex() {
		return (this.page - 1) * this.rows;
	}
	
	//获取总页数
	public int getTotalPage() {
		if (this.getTotal() % this.rows == 0) {
			return this.getTotal() / this.rows;
		} else {
			return this.getTotal() / this.rows + 1;
		}
	}

	//上一页
	public int getPreviousPage() {
		return this.page - 1 > 0 ? this.page - 1 : 1;
	}
	
	//下一页
	public int getNextPage() {
		return this.page + 1 > getTotalPage() ? getTotalPage() : this.page + 1;
	}

}

3.3 后台分页数据查询
3.3.1 处理流程
(1) 查询满足条件的总记录数
(2) 查询满足条件的当前页的数据
(3) 上两个步骤的查询条件要一致

3.3.2 实现
1) 为简化数据库操作,需要一个DBUtil工具类`

public final class DBUtil {

    private static String DRIVER_NAME = "com.mysql.jdbc.Driver";

    private static String DB_URL = "jdbc:mysql://localhost:3306/t234?useUnicode=true&characterEncoding=utf-8&useSSL=false";

    private static String DB_USER = "root";

    private static String DB_PASSWORD = "123456";


    private DBUtil() {
    }


    static {
        try {
            Class.forName(DRIVER_NAME);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    public static Connection getConection() throws SQLException {
        Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
        return connection;
    }


    public static void closeDB(ResultSet rs, Statement ps, Connection con) {

        try {
            if (rs != null && !rs.isClosed()) {
                rs.close();
            }

            if (ps != null && !ps.isClosed()) {
                ps.close();
            }

            if(con != null && !con.isClosed()) {
                con.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    
    public static void closeDB(ResultSet rs, Statement ps) {

        try {
            if (rs != null && !rs.isClosed()) {
                rs.close();
            }

            if (ps != null && !ps.isClosed()) {
                ps.close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

(2)分页查询

public class StudentDao {

	public List<Student> getStudents(String sname, PageBean pageBean) {

		String sql = "select * from t_student t ";

		if (!Objects.isNull(sname) && sname.length() > 0) {
			sql += " where t.sname like ?";
		}
		
		List<Student> students = new ArrayList<>();

		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		/*
		 * 如果不需要分页,则直接查询查询,并返回查询结果。
		 */
		if (pageBean == null || !pageBean.isPagination()) {
			//3. 
			try {
				con = DBUtil.getConection();
				ps = con.prepareStatement(sql);
				
				//设置查询参数
				if(sname != null) {
					ps.setObject(1, sname+"%");
				}
				
				rs = ps.executeQuery();
				
				//1. 差异化部分
				while(rs.next()) {
					Student stu = new Student();
					stu.setSid(rs.getInt("sid"));
					stu.setSname(rs.getString("sname"));
					stu.setAge(rs.getInt("age"));
					stu.setRemark(rs.getString("remark"));
					students.add(stu);
				}
			} catch (Exception e) {
				e.printStackTrace();
				//2.
			} finally  {
				DBUtil.closeDB(rs, ps, con);
			}
			
			return students;
			
		} else {
			
			/*
			 * 如果需要分页,则分两步操作:
			 * 1. 查询总记录数
			 * 2. 查询当前页记录
			 */
			
			//1. 生成统计总记录数的SQL, 查询总记录数
			try {
				String countSql = "select count(*) from (" + sql + ") tmp";
				con = DBUtil.getConection();
				
				ps = con.prepareStatement(countSql);
				
				//设置查询参数
				if(sname != null) {
					ps.setObject(1, sname+"%");
				}
				
				rs = ps.executeQuery();
				
				while(rs.next()) {
					pageBean.setTotal(rs.getInt(1));
				}
				
				/*
				 * 如果统计的总记录数为0,则表示没有符合条件的记录,直接返回一个空结果集即可。
				 */
				if(pageBean.getTotal() == 0) {
					return students;
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				DBUtil.closeDB(rs, ps);
			}
			
			/*
			 * 生成分页sql,查询当前页的数据
			 */
			try {
				String pagingSql = sql + " limit " + pageBean.getStartRow() + ", " + pageBean.getRows();
				ps = con.prepareStatement(pagingSql);
				
				//设置查询参数
				if(sname != null) {
					ps.setObject(1, sname+"%");
				}
				rs = ps.executeQuery();
				
				//差异化
				while(rs.next()) {
					Student stu = new Student();
					stu.setSid(rs.getInt("sid"));
					stu.setSname(rs.getString("sname"));
					stu.setAge(rs.getInt("age"));
					stu.setRemark(rs.getString("remark"));
					students.add(stu);
				}
			} catch (Exception  e) {
				e.printStackTrace();
			} finally {
				DBUtil.closeDB(rs, ps, con);
			}
			
			return students;
		}
	}
	
	public static void main(String[] args) {
		StudentDao dao = new StudentDao();
		List<Student> students = dao.getStudents("张", new PageBean());
		
		students.forEach(s -> System.out.println(s));
	}

}

Student实体,及对应的数据库表可自行准备。

**3.3.3 重构-提取公用方法
(1)为了进行公共方法的抽取,需要找出上面实习中的可通用部分,和差异化部分。

只要是分页,就会统计总记录数,而总记录数的统计是在业务sql外封装了一个select count(*)是有规律可循的,可以通用

只要是分页,则封装分页sql也是有规律可循的(在业务sql后加limit子句即可),可以通用

因为每个查询对应的业务实体(即模型)不同,所以ORM映射部分不能通用

(2)公用方法封装思路

将可通用的部分封装到模板中

差异化部分(即不可通用部分),可以定义一个处理接口,以便于通过参数传入个性化的实现部分

(3) 具体实现
通用分页查询模板类:

public final class DBTemplate {
	
	private DBTemplate() {
	}
	
	public static interface IORMConvert<T> {
		List<T> convert(ResultSet rs) throws SQLException;
	}
	
	public static <T> List<T> query(String sql, 
			Object[] params, 
			PageBean pageBean, 
			IORMConvert<T> convert) {
		
		List<T> datas = new ArrayList<>();
		
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		//不需要分页
		if (pageBean == null || !pageBean.isPagination()) {
			try {
				con = DBUtil.getConection();
				ps = con.prepareStatement(sql);
				
				setParam(params, ps);
				
				rs = ps.executeQuery();
				
				datas = convert.convert(rs);
				
				return datas;
			} catch(Exception e) {
				e.printStackTrace();
			} finally {
				DBUtil.closeDB(rs, ps, con);
			}
			
		} else {
			//1. 查询总记录数
			//2. 查询当前页数据
			
			//1. 生成统计总记录数的SQL, 查询总记录数
			try {
				String countSql = "select count(*) from (" + sql + ") tmp";
				con = DBUtil.getConection();
				
				ps = con.prepareStatement(countSql);
				
				setParam(params, ps);
				
				rs = ps.executeQuery();
				
				while(rs.next()) {
					pageBean.setTotal(rs.getInt(1));
				}
				
				/*
				 * 如果统计的总记录数为0,则表示没有符合条件的记录,直接返回一个空结果集即可。
				 */
				if(pageBean.getTotal() == 0) {
					return datas;
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if(pageBean.getTotal() == 0) {
					DBUtil.closeDB(rs, ps, con);
				}
				DBUtil.closeDB(rs, ps);
			}
			
			//查询当前页数据
			try {
				String pagingSql = sql 
						+ " limit " 
						+ pageBean.getStartRow() 
						+ ", " 
						+ pageBean.getRows();
				ps = con.prepareStatement(pagingSql);
				
				setParam(params, ps);
				
				rs = ps.executeQuery();
				
				datas = convert.convert(rs);
				
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				DBUtil.closeDB(rs, ps, con);
			}
			
		}
		
		return datas;
	}

	private static void setParam(Object[] params, PreparedStatement ps) throws SQLException {
		if (params != null) {
			int i = 1;
			for (Object param : params) {
				ps.setObject(i, param);
				i++;
			}
		}
	}

}

使用示例:

public class StudentDao2 {
	
	public List<Student> getStudents(String sname, PageBean pageBean) {
		
		String sql = "select * from t_student where sname like ?";
		
		return DaoTemplate.query(sql, new Object[] {sname}, pageBean, new IORMConvert<Student>() {
			
			@Override
			public List<Student> convert(ResultSet rs) throws SQLException {
				List<Student> stus = new ArrayList<>();
				while(rs.next()) {
					Student stu = new Student();
					stu.setSid(rs.getInt("sid"));
					stu.setSname(rs.getString("sname"));
					stu.setAge(rs.getInt("age"));
					stu.setRemark(rs.getString("remark"));
					stus.add(stu);
				}
				
				return stus;
			}
		});
		
	}
	
	public static void main(String[] args) {
		StudentDao2 dao = new StudentDao2();
		PageBean pageBean = new PageBean();
		pageBean.setPage(3);
		List<Student> students = dao.getStudents("张%", pageBean);
		students.forEach(s -> System.out.println(s));
	}

}

3.4 分页标签
3.4.1 准备一个Servlet
准备一个servlet用于处理请求,获取数据库中的数据,并转发到结果显示页面。

@WebServlet(value = "/students")
public class StudentAction extends HttpServlet {
		
	private static final long serialVersionUID = 3152900867611381148L;
	private StudentDao2 studentDao = new StudentDao2();

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		doPost(request, response);
	}
	
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		
		PageBean pageBean = new PageBean();
		pageBean.setRequest(request);
		request.setAttribute("pageBean", pageBean);
		
		String sname = request.getParameter("sname");
		List<Student> students = studentDao.getStudents(sname+"%", pageBean);
		request.setAttribute("students", students);
		
		System.out.println("dopost .......... ");
		request.getRequestDispatcher("/students/stuList.jsp").forward(request, response);
	}
	
}

3.4.2 结果展示页面
创建一个页面,该页面用于显示结果, 使用jstl的c标签来展示结果,为正常使用c标签,需要引入jstl-1.2.jar和standard-1.1.2.jar。

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="z" uri="/zking" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<h1>学生信息</h1>
	
	<form action="<%=request.getContextPath()%>/students" method="post">
		<input type="text" name="sname">
		<input type="submit" value="查询">
	</form>
	
	<table border="1" style="width: 98%;">
	
		<tr>
			<td>学号</td>
			<td>姓名</td>
			<td>年龄</td>
			<td>备注</td>
		</tr>
		
		<c:forEach items="${students}" var="student">
			<tr>
				<td>${student.sid}</td>
				<td>${student.sname}</td>
				<td>${student.age}</td>
				<td>${student.remark}</td>
			</tr>
		</c:forEach>
		
	</table>
</body>
</html>

3.4.3 过滤器解决中文乱码问题

/**
 * 中文乱码处理
 */
 @WebFilter("/*")
public class EncodingFiter implements Filter {

	private String encoding = "UTF-8";// 默认字符集

	public EncodingFiter() {
		super();
	}

	public void destroy() {
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;

		// 中文处理必须放到 chain.doFilter(request, response)方法前面
		res.setContentType("text/html;charset=" + this.encoding);
		if (req.getMethod().equalsIgnoreCase("post")) {
			req.setCharacterEncoding(this.encoding);
		} else {
			Map map = req.getParameterMap();// 保存所有参数名=参数值(数组)的Map集合
			Set set = map.keySet();// 取出所有参数名
			Iterator it = set.iterator();
			while (it.hasNext()) {
				String name = (String) it.next();
				String[] values = (String[]) map.get(name);// 取出参数值[注:参数值为一个数组]
				for (int i = 0; i < values.length; i++) {
					values[i] = new String(values[i].getBytes("ISO-8859-1"),
							this.encoding);
				}
			}
		}

		chain.doFilter(request, response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		String s = filterConfig.getInitParameter("encoding");// 读取web.xml文件中配置的字符集
		if (null != s && !s.trim().equals("")) {
			this.encoding = s.trim();
		}
	}

}

3.4.4 加入分页功能
1)先不考虑功能性在页面上的table标签下,加入及分页工具条

<div style="text-align: right; width:98%;">
		第1页&nbsp;&nbsp;&nbsp;
		共100条记录&nbsp;&nbsp;&nbsp;
		<a>首页</a>&nbsp;&nbsp;&nbsp;
		<a>上页</a>&nbsp;&nbsp;&nbsp; 
		<a>下页</a>&nbsp;&nbsp;&nbsp; 
		<a>尾页</a>&nbsp;&nbsp;&nbsp;<input type="text" size="2" /> 
		<a href="#">GO</a>
	</div>

2)不考虑通过的分页如下实现,先在结果页面中实现分页功能。`

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="z" uri="/zking" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<h1>学生信息</h1>
	
	<form action="<%=request.getContextPath()%>/students" method="post">
		<input type="text" name="sname"/>
		<input type="submit" value="查询">
	</form>
	
	<table border="1" style="width: 98%;">
	
		<tr>
			<td>学号</td>
			<td>姓名</td>
			<td>年龄</td>
			<td>备注</td>
		</tr>
		
		<c:forEach items="${students}" var="student">
			<tr>
				<td>${student.sid}</td>
				<td>${student.sname}</td>
				<td>${student.age}</td>
				<td>${student.remark}</td>
			</tr>
		</c:forEach>
	</table>
	
	<!-- 分页页面元素 -->
	<div style="text-align: right; width:98%;">
		第${pageBean.page}页&nbsp;&nbsp;&nbsp;
		共${pageBean.total}条记录&nbsp;&nbsp;&nbsp;
		<a href="javascript: goPage(1);">首页</a>&nbsp;&nbsp;&nbsp;
		<a href="javascript: goPage('${pageBean.previousPage}');">上页</a>&nbsp;&nbsp;&nbsp; 
		<a href="javascript: goPage('${pageBean.nextPage}');">下页</a>&nbsp;&nbsp;&nbsp; 
		<a href="javascript: goPage('${pageBean.totalPage}')">尾页</a>&nbsp;&nbsp;&nbsp;<input type="text" id="pagingPageNum" size="2" onkeypress="goSpecifiedPage(event,this.value);"/> 
		<a href="javascript: goPage(document.getElementById('pagingPageNum').value)">GO</a>
	</div>
	
	<!-- 用于分页的隐藏表单 -->
	<form action="${pageBean.url}" id="pagingForm" method="post">
		<input type="hidden" name="page" value="${pageBean.page}"/>
		<!-- 先只考虑本功能的查询参数,没有考虑公用性(不同功能的参数不同) -->
		<input type="hidden" name="sname" value="<%=request.getParameter("sname")%>"/>
	</form>
	
	<!-- 用于分页的js代码 -->
	<script>
	function goPage(pageNum) {
		var form = document.getElementById("pagingForm");
		form.page.value = pageNum;
		form.submit();
	}
	
	function goSpecifiedPage(event) {
		if(event.keyCode == 13) {
			var pageNum = document.getElementById("pagingPageNum").value;
			var form = document.getElementById("pagingForm");
			form.page.value = pageNum;
			form.submit();
		}
	}
	</script>
	
</body>
</html>

目前为止,分页功能已经实现了。

遗留下来的问题
1)如果其他功能需要分页,则需要复制大量代码才能重用该功能
2)如果系统需要修改分页工具栏的显示风格呢?

3.4.4 封装分页标签
为了方便代码的复用,及可维护性,我们将分页功能封装了一个自定义标签(其实就是将原来写在页面中的代码,通过移入到自定义标签中去实现),开发自定义标签分成三步:

1.编写助手类
2.编写标签描述文件
3.在页面上引入标签库,并使用

(1) 编写助手类

public class PagingTag extends BodyTagSupport {
	
	private PageBean pageBean;

	public PageBean getPageBean() {
		return pageBean;
	}

	public void setPageBean(PageBean pageBean) {
		this.pageBean = pageBean;
	}
	
	@Override
	public int doStartTag() throws JspException {
		
		JspWriter out = this.pageContext.getOut();
		
		try {
			out.println(buildHtml());
			return SKIP_BODY;
		} catch (IOException e) {
			throw new JspException("分页标签异常", e);
		}
		
	}
	
	//生成Html内容
	private String buildHtml() {
		
		//构建分页页面元素
		String pagingElement = "<div style=\"text-align: right; width:98%;\">\r\n" + 
				"		第"  + pageBean.getPage() + "页&nbsp;&nbsp;&nbsp;\r\n" + 
				"		共" + pageBean.getTotal() + "条记录&nbsp;&nbsp;&nbsp;\r\n" + 
				"		<a href=\"javascript: goPage(1);\">首页</a>&nbsp;&nbsp;&nbsp;\r\n" + 
				"		<a href=\"javascript: goPage('" + pageBean.getPreviousPage() + "');\">上页</a>&nbsp;&nbsp;&nbsp; \r\n" + 
				"		<a href=\"javascript: goPage('" + pageBean.getNextPage() + "');\">下页</a>&nbsp;&nbsp;&nbsp; \r\n" + 
				"		<a href=\"javascript: goPage('" + pageBean.getTotalPage() + "')\">尾页</a>&nbsp;&nbsp;&nbsp;\r\n" + 
				"		第<input type=\"text\" id=\"pagingPageNum\" size=\"2\" value='"+pageBean.getPage()+"' οnkeypress=\"goSpecifiedPage(event,this.value);\"/> \r\n" + 
				"		<a href=\"javascript: goPage(document.getElementById('pagingPageNum').value)\">GO</a>\r\n" + 
				"	</div>";
		
		//构建隐藏表单,用于在分页时传递分页参数
		String hiddenForm = "<form action='" + pageBean.getUrl() + "' id=\"pagingForm\" method=\"post\">"
				+ "<input type=\"hidden\" name=\"page\" />";
		Map<String, String[]> parameterMap = pageBean.getParameterMap();
		for(Map.Entry<String, String[]> param: parameterMap.entrySet()) {
			String paramName = param.getKey();
			if("page".equals(paramName)) continue;
			String[] values = param.getValue();
			for(String val:  values) {
				hiddenForm += "<input type='hidden' name='" + paramName + "' value='" + val + "'>";
			}
		}
		hiddenForm += "</form>";
		
		//构建分页功能需要的js代码块
		String script = "<script>\r\n" + 
				"	function goPage(pageNum) {\r\n" + 
				"		var form = document.getElementById(\"pagingForm\");\r\n" + 
				"		form.page.value = pageNum;\r\n" + 
				"		form.submit();\r\n" + 
				"	}\r\n" + 
				"	\r\n" + 
				"	function goSpecifiedPage(event) {\r\n" + 
				"		if(event.keyCode == 13) {\r\n" + 
				"			var pageNum = document.getElementById(\"pagingPageNum\").value;\r\n" + 
				"			var form = document.getElementById(\"pagingForm\");\r\n" + 
				"			form.page.value = pageNum;\r\n" + 
				"			form.submit();\r\n" + 
				"		}\r\n" + 
				"	}\r\n" + 
				"	</script>";
		
		return pagingElement + hiddenForm + script;
	}
	

}

(2)标签库描述文件中添加paging标签

<tag>
		<name>paging</name>
		<tag-class>com.zking.mvc.tag.PagingTag</tag-class>
		<body-content>empty</body-content>
		<attribute>
			<name>pageBean</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>

(3) 使用分页标签
首先在页面中引入标签

<%@taglib prefix="z" uri="/zking" %>

将原来的分页功能,替换为标签即可

<z:paging pageBean="${pageBean}"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值