为简化分页功能,设计了一个分页的JSP标签,只需要在页面使用分页标签,就可以完成所有页面的分页功能。
1. 项目结构和数据库设计
(1) 项目结构:
(2) 数据库设计
2. PageModel.java和PagerTag.java
(1) PageModel.java
/** * 分页实体 */ public class PageModel { // 默认每页4条数据 public static int PAGE_DEFAULT_SIZE = 4; /** 分页总数据条数 */ private int recordCount; /** 当前页面 */ private int pageIndex ; /** 每页分多少条数据 */ private int pageSize = PAGE_DEFAULT_SIZE = 4; /** 总页数 */ private int totalSize; public int getRecordCount() { this.recordCount = this.recordCount <= 0 ? 0:this.recordCount; return recordCount; } public void setRecordCount(int recordCount) { this.recordCount = recordCount; } public int getPageIndex() { this.pageIndex = this.pageIndex <= 0?1:this.pageIndex; /** 判断当前页面是否超过了总页数:如果超过了默认给最后一页作为当前页 */ this.pageIndex = this.pageIndex>=this.getTotalSize()?this.getTotalSize():this.pageIndex; return pageIndex; } public void setPageIndex(int pageIndex) { this.pageIndex = pageIndex; } public int getPageSize() { this.pageSize = this.pageSize <= PAGE_DEFAULT_SIZE?PAGE_DEFAULT_SIZE:this.pageSize; return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTotalSize() { if(this.getRecordCount() <=0){ totalSize = 0 ; }else{ totalSize = (this.getRecordCount() -1)/this.getPageSize() + 1; } return totalSize; } public int getFirstLimitParam(){ return (this.getPageIndex()-1)*this.getPageSize() ; } }
(2) PagerTag.java
package com.game.util; import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.SimpleTagSupport; /** * 分页标签 */ public class PagerTag extends SimpleTagSupport { /** 定义请求URL中的占位符常量 */ private static final String TAG = "{0}"; /** 当前页码 */ private int pageIndex; /** 每页显示的数量 */ private int pageSize; /** 总记录条数 */ private int recordCount; /** 请求URL page.action?pageIndex={0}*/ private String submitUrl; /** 样式 */ private String style = "sabrosus"; /** 定义总页数 */ private int totalPage = 0; /** 在页面上引用自定义标签就会触发一个标签处理类 */ @Override public void doTag() throws JspException, IOException { /** 定义它拼接是终的结果 */ StringBuilder res = new StringBuilder(); /** 定义它拼接中间的页码 */ StringBuilder str = new StringBuilder(); /** 判断总记录条数 */ if (recordCount > 0){ //1499 / 15 = 100 /** 需要显示分页标签,计算出总页数 需要分多少页 */ totalPage = (this.recordCount - 1) / this.pageSize + 1; /** 判断上一页或下一页需不需要加a标签 */ if (this.pageIndex == 1){ // 首页 str.append("<span class='disabled'>上一页</span>"); /** 计算中间的页码 */ this.calcPage(str); /** 下一页需不需要a标签 */ if (this.pageIndex == totalPage){ /** 只有一页 */ str.append("<span class='disabled'>下一页</span>"); }else{ String tempUrl = this.submitUrl.replace(TAG, String.valueOf(pageIndex + 1)); str.append("<a href='"+ tempUrl +"'>下一页</a>"); } }else if (this.pageIndex == totalPage){ // 尾页 String tempUrl = this.submitUrl.replace(TAG, String.valueOf(pageIndex - 1)); str.append("<a href='"+ tempUrl +"'>上一页</a>"); /** 计算中间的页码 */ this.calcPage(str); str.append("<span class='disabled'>下一页</span>"); }else{ // 中间 String tempUrl = this.submitUrl.replace(TAG, String.valueOf(pageIndex - 1)); str.append("<a href='"+ tempUrl +"'>上一页</a>"); /** 计算中间的页码 */ this.calcPage(str); tempUrl = this.submitUrl.replace(TAG, String.valueOf(pageIndex + 1)); str.append("<a href='"+ tempUrl +"'>下一页</a>"); } /** 拼接其它的信息 */ res.append("<table width='100%' align='center' style='font-size:13px;' class='"+ style +"'>"); res.append("<tr><td style='COLOR: #0061de; MARGIN-RIGHT: 3px; PADDING-TOP: 2px; TEXT-DECORATION: none'>" + str.toString()); res.append(" 跳转到 <input style='text-align: center;BORDER-RIGHT: #aaaadd 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #aaaadd 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 2px; MARGIN: 2px; BORDER-LEFT: #aaaadd 1px solid; COLOR: #000099; PADDING-TOP: 2px; BORDER-BOTTOM: #aaaadd 1px solid; TEXT-DECORATION: none' type='text' size='2' id='pager_jump_page_size'/>"); res.append(" <input type='button' style='text-align: center;BORDER-RIGHT: #dedfde 1px solid; PADDING-RIGHT: 6px; BACKGROUND-POSITION: 50% bottom; BORDER-TOP: #dedfde 1px solid; PADDING-LEFT: 6px; PADDING-BOTTOM: 2px; BORDER-LEFT: #dedfde 1px solid; COLOR: #0061de; MARGIN-RIGHT: 3px; PADDING-TOP: 2px; BORDER-BOTTOM: #dedfde 1px solid; TEXT-DECORATION: none' value='确定' id='pager_jump_btn'/>"); res.append("</td></tr>"); res.append("<tr align='center'><td style='font-size:13px;'><tr><td style='COLOR: #0061de; MARGIN-RIGHT: 3px; PADDING-TOP: 2px; TEXT-DECORATION: none'>"); /** 开始条数 */ int startNum = (this.pageIndex - 1) * this.pageSize + 1; /** 结束条数 */ int endNum = (this.pageIndex == this.totalPage) ? this.recordCount : this.pageIndex * this.pageSize; res.append("总共<font color='red'>"+ this.recordCount +"</font>条记录,当前显示"+ startNum +"-"+ endNum +"条记录。"); res.append("</td></tr>"); res.append("</table>"); res.append("<script type='text/javascript'>"); res.append(" document.getElementById('pager_jump_btn').onclick = function(){"); res.append(" var page_size = document.getElementById('pager_jump_page_size').value;"); res.append(" if (!/^[1-9]\d*$/.test(page_size) || page_size < 1 || page_size > "+ this.totalPage +"){"); res.append(" alert('请输入[1-"+ this.totalPage +"]之间的页码!');"); res.append(" }else{"); res.append(" var submit_url = '" + this.submitUrl + "';"); res.append(" window.location = submit_url.replace('"+ TAG +"', page_size);"); res.append(" }"); res.append("}"); res.append("</script>"); }else{ res.append("<table align='center' style='font-size:13px;'><tr><td style='COLOR: #0061de; MARGIN-RIGHT: 3px; PADDING-TOP: 2px; TEXT-DECORATION: none'>总共<font color='red'>0</font>条记录,当前显示0-0条记录。</td></tr></table>"); } this.getJspContext().getOut().print(res.toString()); } /** 计算中间页码的方法 */ private void calcPage(StringBuilder str) { /** 判断总页数 */ if (this.totalPage <= 11){ /** 一次性显示全部的页码 */ for (int i = 1; i <= this.totalPage; i++){ if (this.pageIndex == i){ /** 当前页码 */ str.append("<span class='current'>"+ i +"</span>"); }else{ String tempUrl = this.submitUrl.replace(TAG, String.valueOf(i)); str.append("<a href='"+ tempUrl +"'>"+ i +"</a>"); } } }else{ /** 靠近首页 */ if (this.pageIndex <= 8){ for (int i = 1; i <= 10; i++){ if (this.pageIndex == i){ /** 当前页码 */ str.append("<span class='current'>"+ i +"</span>"); }else{ String tempUrl = this.submitUrl.replace(TAG, String.valueOf(i)); str.append("<a href='"+ tempUrl +"'>"+ i +"</a>"); } } str.append("..."); String tempUrl = this.submitUrl.replace(TAG, String.valueOf(this.totalPage)); str.append("<a href='"+ tempUrl +"'>"+ this.totalPage +"</a>"); } /** 靠近尾页 */ else if (this.pageIndex + 8 >= this.totalPage){ String tempUrl = this.submitUrl.replace(TAG, String.valueOf(1)); str.append("<a href='"+ tempUrl +"'>1</a>"); str.append("..."); for (int i = this.totalPage - 10; i <= this.totalPage; i++){ if (this.pageIndex == i){ /** 当前页码 */ str.append("<span class='current'>"+ i +"</span>"); }else{ tempUrl = this.submitUrl.replace(TAG, String.valueOf(i)); str.append("<a href='"+ tempUrl +"'>"+ i +"</a>"); } } } /** 在中间 */ else{ String tempUrl = this.submitUrl.replace(TAG, String.valueOf(1)); str.append("<a href='"+ tempUrl +"'>1</a>"); str.append("..."); for (int i = this.pageIndex - 4; i <= this.pageIndex + 4; i++){ if (this.pageIndex == i){ /** 当前页码 */ str.append("<span class='current'>"+ i +"</span>"); }else{ tempUrl = this.submitUrl.replace(TAG, String.valueOf(i)); str.append("<a href='"+ tempUrl +"'>"+ i +"</a>"); } } str.append("..."); tempUrl = this.submitUrl.replace(TAG, String.valueOf(this.totalPage)); str.append("<a href='"+ tempUrl +"'>"+ this.totalPage +"</a>"); } } } /** setter 方法 */ public void setPageIndex(int pageIndex) { this.pageIndex = pageIndex; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public void setRecordCount(int recordCount) { this.recordCount = recordCount; } public void setSubmitUrl(String submitUrl) { this.submitUrl = submitUrl; } public void setStyle(String style) { this.style = style; } }
3. 要使用JSP的标签,还需要在WEB-INF下增加一个tld标签文件:page.tld
<?xml version="1.0" encoding="utf-8"?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <!-- 描述 自定义标签版本的一种描述 --> <description>Pager 1.0 core library</description> <!-- 显示的名称 导包进行的一个展示 --> <display-name>Pager core</display-name> <!-- 版本号 --> <tlib-version>1.0</tlib-version> <!-- 短名 --> <short-name>fkjava</short-name> <!-- uri :导包 --> <uri>/pager-tags</uri> <!-- 定义一个标签 --> <tag> <!-- 标签名 --> <name>pager</name> <!-- 标签处理类,根据自己的实际文件目录更改 --> <tag-class>com.game.util.PagerTag</tag-class> <!-- 设置标签为空 --> <body-content>empty</body-content> <!-- 定义标签的属性 --> <attribute> <!-- 属性名 表示分页的第几页 --> <name>pageIndex</name> <!-- 必须的 --> <required>true</required> <!-- run time expression value 为true支持EL表达式 --> <rtexprvalue>true</rtexprvalue> </attribute> <!-- 定义标签的属性 --> <attribute> <!-- 属性名 表示分页标签 ,每页显示多少条数据 --> <name>pageSize</name> <!-- 必须的 --> <required>true</required> <!-- run time expression value 为true支持EL表达式 --> <rtexprvalue>true</rtexprvalue> </attribute> <!-- 定义标签的属性 --> <attribute> <!-- 属性名 记录分页的总数 --> <name>recordCount</name> <!-- 必须的 --> <required>true</required> <!-- run time expression value 为true支持EL表达式 --> <rtexprvalue>true</rtexprvalue> </attribute> <!-- 定义标签的属性 --> <attribute> <!-- 属性名 --> <name>submitUrl</name> <!-- 必须的 --> <required>true</required> <!-- run time expression value 为true支持EL表达式 --> <rtexprvalue>true</rtexprvalue> </attribute> <!-- 定义标签的属性 --> <attribute> <!-- 属性名 --> <name>style</name> <!-- 必须的 --> <required>false</required> <!-- run time expression value 为true支持EL表达式 --> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>
4. notice.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="fkjava" uri="/pager-tags" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="f" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:set var="ctx" value="${pageContext.request.contextPath}"/> <!--其他代码省略--> <table> <tr> <th>编号</th> <th>标题</th> </tr> <!--其他代码省略--> <!-- 分页标签 --> <tr valign="top"> <td align="center" class="font3"> <fkjava:pager pageIndex="${requestScope.pageModel.pageIndex}" pageSize="${requestScope.pageModel.pageSize}" recordCount="${requestScope.pageModel.recordCount}" style="digg" submitUrl="${ctx}/notice?pageIndex={0}" /> </td>
<!-- 点击一次分页标签就重新查询一次数据库 --> </tr> <!--其他代码省略-->
5. NoticeMapper和NoticesDynaSqlProvider.java
(1) NoticeMapper
// 动态查询 @SelectProvider(type=NoticesDynaSqlProvider.class,method="selectWithParam") List<Notices> selectByPage(Map<String, Object> params); //查询公告数量 @SelectProvider(type=NoticesDynaSqlProvider.class,method="count") Integer count(Map<String, Object> params);
(2) NoticesDynaSqlProvider.java
// 分页动态查询 public String selectWithParam(final Map<String, Object> params){ String sql = new SQL(){ { SELECT("*"); FROM("notices"); if(params.get("notice") != null){ Notices notice = (Notices)params.get("notice"); if(notice.getNoticeName() != null && !notice.getNoticeName().equals("")){ WHERE(" noticeName LIKE CONCAT('%',#{notice.noticeName},'%') "); } if(notice.getNoticeContent() != null && !notice.getNoticeContent().equals("")){ WHERE(" noticeContent LIKE CONCAT('%',#{notice.noticeContent},'%') "); } } } }.toString(); if(params.get("pageModel") != null){ sql += " limit #{pageModel.firstLimitParam} , #{pageModel.pageSize} "; } return sql; } // 动态查询总数量 public String count(final Map<String, Object> params){ return new SQL(){ { SELECT("count(*)");//查询有多少行 FROM("notices"); if(params.get("notice") != null){ Notices notice = (Notices)params.get("notice"); if(notice.getNoticeName() != null && !notice.getNoticeName().equals("")){ WHERE(" noticeName LIKE CONCAT('%',#{notice.noticeName},'%') "); } if(notice.getNoticeContent() != null && !notice.getNoticeContent().equals("")){ WHERE(" noticeContent LIKE CONCAT('%',#{notice.noticeContent},'%') "); } } } }.toString(); }
6. TestService.java和TestServiceImpl.java
(1) TestService.java
/** * 查询所有公告 * @return Notices对象的List集合 */ List<Notices> findNotices(Notices notices, PageModel pageModel);
(2) TestServiceImpl.java
@Transactional(readOnly=true) @Override public List<Notices> findNotices(Notices notices, PageModel pageModel) { // TODO Auto-generated method stub /** 当前需要分页的总数据条数 */ Map<String,Object> params = new HashMap<>(); params.put("notice", notices); //调用count方法 int recordCount = noticesMapper.count(params);//一共多少条数据 pageModel.setRecordCount(recordCount); if(recordCount > 0){ /** 开始分页查询数据:查询第几页的数据 */ params.put("pageModel", pageModel); } List<Notices> notices1 = noticesMapper.selectByPage(params); return notices1; }
7. NoticeController.java
@RequestMapping(value="/notice") /** * 处理查询请求 * @param model * @param pageIndex * @param notice * @return */ public String notice(Model model,Integer pageIndex, @ModelAttribute Notices notice){ PageModel pageModel = new PageModel(); if(pageIndex != null){ pageModel.setPageIndex(pageIndex); } /** 查询notices信息 */ List<Notices> notices = testService.findNotices(notice, pageModel);//根据输入的公告名称或公告内容,查询公告信息 model.addAttribute("notices", notices); model.addAttribute("pageModel", pageModel); return "notice"; }
8. Notices.java
public class Notices implements Serializable{ private Integer noticeID; private String noticeName;//通知名称 private String noticeContent;//通知内容 private Date noticeTime;//通知时间 //空的构造方法 public Notices(){ super(); } //getter and setter }
9. 另外所需的文件:
(1) pager.css
(2) fkjava.ico
以上两个文件可在这里下载
效果如图:
改进:可在web.xml中配置来给所有的.jsp文件导入要依赖的库,这样就不用在每个.jsp页面都导入一遍要依赖的库了。
1. web.xml
<!-- jsp的配置 --> <jsp-config> <jsp-property-group> <!-- 配置拦截所有的jsp页面 --> <url-pattern>*.jsp</url-pattern> <!-- 可以使用el表达式 --> <el-ignored>false</el-ignored> <!-- 不能在页面使用java脚本 --> <scripting-invalid>true</scripting-invalid> <!-- 给所有的jsp页面导入要依赖的库,tablib.jsp就是一个全局的标签库文件 --> <include-prelude>/WEB-INF/jsp/taglib.jsp</include-prelude> </jsp-property-group> </jsp-config>
2. taglib.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="f" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 设置一个项目路径的变量 -->
<c:set var="ctx" value="${pageContext.request.contextPath}"></c:set>
<!-- 配置分页标签 -->
<%@ taglib prefix="fkjava" uri="/pager-tags" %>
参考:《Spring+MyBatis企业应用实战》
------------------------------------------------------------------分割线-------------------------------------------------------------------
MySQL中limit的使用:
select * from notices limit 5 //获取notices表中的前5条数据 select * from notices limit 3, 4 //获取notices表从第3条记录(不包括第3条记录)开始,检索4条记录 select * from notices order by noticeID desc limit 1 //获取notices表中的最后一条数据 select * from notices limit 1 //获取notices表的第一条数据
MySQL中的分页:在SQL语句中得到是从哪条记录开始,取多少个记录。例:页长为3,每页有4条记录。那么第2页就是从第5条记录开始,一共取4条记录。
select * from notices limit 4, 4 //扫描满足条件的8行数据,扔掉前面的4行,返回最后的4行