部门成立不久, 还没有自己统一的分页规范, 因此借这个机会自己编写了一个可以复用的小分页组件JPage.
1. 概述
分页是一般的web系统不可缺少的功能之一, 主要设计到两个主题: 1, 解决针对不同RDBMS产品的分页方法(SQL语句)的问题; 2, 前端分页展示的代码. 针对第一个问题可以使用Hibernate的分页查询功能; 第二的问题, 可以自己在JSP页面里面编写SQL脚本合理地显示结果和控制元素, 但是如果将遍历集合的Java代码嵌入JSP页面, 那么页面的代码将会不怎么规范和友好, 当然这个问题可以借助于当前一些强大的开源框架予以解决, 例如部门项目广泛使用的pager-taglib.
但是目前也有存在一些问题. 前期公司推广SNF框架, 数据访问层使用DAL组件, 其中的SQL语句(包括分页SQL语句)是要程序员自己编写, 但是分页语法不属于SQL规范定义的范畴, 所以不同RDBMS的分页语法差别很大, 即使是同一款RDBMS也有许多不同的分页方式, 其中性能差别很大. 以DB2为例, 之前测试的时候两个已经经过优化的分页SQL语句耗时相差也能达到5倍. 关于页面显示方面, 虽然开源框架提供了强大的分页展示功能, 但是分页毕竟是一个很小但又不可缺少的功能, 如果不想引入开源框架而又想实现从Dao层到View层的统一的分页方法可以考虑使用JPage这个类库.
2. 功能
假如你现在需要使用以下SQL查询数据:
“SELECT user_id, user_name FROM user_info where address=? ORDER BY age”
当需要显示一个页面的时候你至少需要提供”每页显示几条记录(pageSize)”以及”我要显示第几页(pageNumber)”这样的信息, 然后最终输出的是你需要的”这一页需要显示这些数据”这个信息. 因此你只需要提供(1)SQL(2)pageSize(3)pageNum, JPage将会根据你连接的数据库JDBC Connection选择合适的也分SQL语句查询需要的数据(如果不提供pageSize和pageNum的值那么将使用默认的值pageSize=10, pageNum=1), 目前仅支持MySQL和DB2(已在MySQL for Windows32, DB2 for Windows32, DB2 for AIX64上测试通过).
典型情况下页面显示主要有一下几个方面:
<!--[if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]-->
其中”导航”部分的效果可以参考百度的搜索结果显示方式(因为说不清). JPage使用JSP自定义表达式的方式实现对数据的显示和控制.
3. 使用
现在以Spring MVC + DB2 For Windows演示JPage的使用, JPage的核心类是com.suning.util.JPage, 通过这个类设置你需要显示的pageSize, SQL语句和pageNum;
从面向对象的角度来说可以把一个页面当做一个对象, 这个对象包含需要显示的数据、当前页码、页大小等信息,这就是com.suning.util.PageBean, 使用
JPage# public PageBean<T> generatePageBean(int pageNum)
这个方法可以产生最终需要显示的PageBean, 即页面对象.
3.1 Dao层
JPage定义了一个接口com.suning.dao.PageDao用于定义使用JPage必须实现的方法用于使你可以根据实际的Dao层组件选择自己定义的分页查询语句(例如使用Hibernate等等), 但是为了保证使用的简单性以及尽量不耦合第三方代码, JPage提供了一个基于JDBC的默认实现: com.suning.dao.impl.jdbc.PageDaoImpl
参考以下配置, 你至少需要提供dataSource
<bean name="pageDao" class="com.suning.dao.impl.jdbc.PageDaoImpl"> <!-- 你需要提供一个DataSource --> <property name="dataSource" ref="dataSource" /> </bean> <bean name="jpage" class="com.suning.util.JPage"> <property name="dao" ref="pageDao" /> </bean>
3.2 Service层
将jpage bean注入给Service类, 我这里使用Annotation方式注入:
@Service("userInfoService") public class UserInfoServiceImpl implements UserInfoService { private static Logger logger = LoggerFactory .getLogger(UserInfoServiceImpl.class); /** * JPage分页组件 */ @Resource(name = "jpage") private JPage<Object[]> jpage; //setter and getter...
在Service层通过注入的JPage实例取得实际需要的数据和相关信息
@Override public PageBean<Object[]> getOperateLogs(String userInfoUUID, Timestamp startTime, Timestamp endTime, int pageSize, int pageNum) throws IFMException { StringBuilder querySQL = new StringBuilder( "SELECT USER_UUID, USER_IP, HOST_NAME, OPERATE_CLASS_NAME, METHOD_NAME, OPERATE_RESULT_FLAG, OPERATE_DATE_TIME, EXTRA_INFO FROM OPERATE_LOG WHERE USER_UUID=?"); if (startTime != null) { querySQL.append(" and OPERATE_DATE_TIME>=? "); } if (endTime != null) { querySQL.append(" and OPERATE_DATE_TIME<=?"); } List<Object> params = new ArrayList<Object>(); params.add(userInfoUUID); if (startTime != null) { params.add(startTime); } if (endTime != null) { params.add(endTime); } // 设置pageSize jpage.setPageSize(pageSize); // 设置实际查询SQL jpage.setSql(querySQL.toString(), params.toArray()); // 返回第pageNum页的数据 return jpage.generatePageBean(pageNum); }
3.3 controller层
3.4 页面
前端显示的JSP标签使用JSP自定义标签实现, 因此需要先引入标签库
<%@ taglib uri="http://www.suning.com/wuliu/jpage-taglib" prefix="jpage"%>
JPage的核心控制标签是<jpage:jpage></jpage:jpage>, 该标签有以下属性:
属性名 | 是否必须 | 含义 | 备注 |
totalpageNum | Y | 总页数 | |
maxShowNum | N | 最大显示页数 | |
url | Y | 需要访问的URL | 需要是相对路径, 属于BUG |
pageNum | Y | 当前页码 |
以上这些属性除了URL之外, 其他完全可以不用配置的, 但是JSP自定义标签需要的一些属性在后台没办法取到, 目前还未想到合理地办法予以解决, 在后期想办法简化这些配置.
<!-- 显示页码以及导航区域 --> <jpage:jpage url="showOperateLog.action" totalPageNum="${pageBean.totalPageNum }" pageNum="${pageBean.pageNum }" pageSize="${pageBean.pageSize }" > <!-- 需要传递的参数 --> <jpage:param name="startTime" value="${startTime }"/> <jpage:param name="endTime" value="${endTime }"/> <jpage:param name="userInfoUUID" value="${userInfo.userInfoUUID}"/> <!-- 通过pageBean获取信息 --> <span>共${pageBean.totalNum }条记录</span> <span>共${pageBean.totalPageNum }页</span> <!-- 导航 --> <!-- 首页 --> <jpage:first> <a href="${pageUrl }">首页</a> </jpage:first> <!-- 上一页 --> <jpage:prev> <a href="${pageUrl }">上一页</a> </jpage:prev> <!-- 显示页 --> <jpage:pages> <c:choose> <c:when test="${index eq pageNum }"> <a href="${indexUrl }"><font color="red" size="5px"> ${index}</font></a> </c:when> <c:otherwise> <a href="${indexUrl }">${index }</a> </c:otherwise> </c:choose> </jpage:pages> <!-- 下一页 --> <jpage:next> <a href="${pageUrl }">下一页</a> </jpage:next> <!-- 末页 --> <jpage:last> <a href="${pageUrl }">末页</a> </jpage:last> </jpage:jpage>
<jpage:jpage/>有以下几个子标签
4. 不足以及问题列表
4.1 自定义标签复杂
在” 代码清单6 使用JPage自定义标签显示分页控制”中可以看到如果使用JPage自定义标签那么代码量比较多(其实与开源分页类库pager-taglib比起来代码量差不多), 这是由于两个方面引起的. 一个是由于在显示”首页”, ”第X页”等等信息的时候有些项目使用JavaScript实现, 有些是使用超链接实现; 而且需要自定义CSS样式, 自定义显示控制等等, 因此就有了<jpage:first>, <jpage:next>这类子标签以提供用户的自定义. 另一方面是<jpage:jpage标签的属性过多>
<jpage:jpage url="showOperateLog.action" totalPageNum="${pageBean.totalPageNum }" pageNum="${pageBean.pageNum }" pageSize="${pageBean.pageSize }" >
这四个属性只有url是需要用户定义的, 其他属性都是固定的可以直接copy, 但是由于水平有限目前还没想到比较好的办法可以消除这些属性的显式配置(开源的pager-taglib是通过配置过滤器的方式消除这些配置的).
其实<jpage:jpage>自定义标签虽然看起来代码多, 但是绝大部分都是可以直接copy使用的, 后期想办法简化.
5. 代码分析
5.1 JPage类
JPage主要的类是com.suning.util.JPage, 源文件如下:
package com.suning.util; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.suning.dao.PageDao; /** * 分页辅助类, 便于方便地取得实际需要显示的页面数据; * * 需要首先初始化一个JPage对象, 需要实际查询的SQL语句, 查询所定义的参数, 每一页的大小, * 以及针对不同RDBMS的实际分页语句;支持通过构造方法初始化, 或者通过setter两种方式初始化JPage对象. * * 初始化JPage对象之后, 通过#getData(int pageNum)方法传入需要取得的页编号, 即可返回需要的数据. * * @author 12072533 * * @param <T> */ public class JPage<T> { private static final Logger logger = LoggerFactory.getLogger(JPage.class); private static final Integer DEFAULT_PAGENUM = 1; private static final Integer DEFAULT_PAGESIZE = 10; private int pageSize = DEFAULT_PAGENUM; private String sql; private Object[] parameters; private PageDao<T> dao; /***************** Two basic constructor ******************/ JPage() { } JPage(int pageSize, String sql, Object... parameters) { setPageSize(pageSize); setSql(sql, parameters); } public void setPageSize(int pageSize) { if (pageSize > 0) { this.pageSize = pageSize; } } /** * 设置需要查询的SQL语句以及需要的参数 * * eg. setSql("SELECT * FROM TB_NAME WHERE COL01>param", new Object[]{10}) * * @param sql * @param parameters */ public void setSql(String sql, Object... parameters) { validateSQL(sql); this.sql = sql; this.parameters = parameters; } public void setDao(PageDao<T> dao) { this.dao = dao; } /** * 取得一个SQL查询的结果集的数量 * * @param sql * @param parameters * @return 查询的结果集的数量 */ protected long getTotalNum(String sql, Object... parameters) { String countSQL = "SELECT COUNT(*) FROM (" + sql + ") AS TEMP"; return this.dao.getTotalNum(countSQL, parameters); } /** * 供客户端代码直接调用, 在初始化JPage对象后, 直接取得结果集数量 * * @return long */ public long getTotalNum() { return getTotalNum(sql, parameters); } /** * 取得指定SQL查询在以pageSize为单位的时候实际的页面总数量 * * @param sql * @param pageSize * 每一页显示的记录数量 * @param parameters * @return Integer */ protected Integer getTotalPageNum(String sql, int pageSize, Object... parameters) { long totalNum = getTotalNum(sql, parameters); int temp = Long.valueOf(totalNum).intValue() % pageSize; Integer totalPageNum = null; if (temp == 0) { totalPageNum = Long.valueOf(totalNum).intValue() / pageSize; } else { totalPageNum = (Long.valueOf(totalNum).intValue() / pageSize) + 1; } return totalPageNum; } /** * 在初始化JPage对象后, 供客户端代码查询页面总数 * * @return 页面的数量 */ public Integer getTotalPageNum() { return getTotalPageNum(sql, pageSize, parameters); } /** * 取得指定页面的记录对象集合 * * @param pageSize * 页面大小 * @param pageNum * 页面编号, 从1开始 * @param sql * @param parameters * @return List<T> */ protected List<T> getData(int pageSize, int pageNum, String sql, Object... parameters) { if (pageNum < 1) { logger.warn("pageNum not properly set, using default page number 1"); pageNum = DEFAULT_PAGENUM; } if (pageSize <= 0) { logger.warn("pageSize not properly set, using default page size 10"); pageSize = DEFAULT_PAGESIZE; } Integer totalPageNum = getTotalPageNum(sql, pageSize, parameters); if (pageNum >= totalPageNum) { pageNum = totalPageNum; } int start = (pageNum - 1) * pageSize; return this.dao.getData(start, pageSize, sql, parameters); } /** * 初始化JPage之后, 直接传入页面编号, 取得页面的记录集合 * * @param pageNum * @return */ public List<T> getData(int pageNum) { return getData(pageSize, pageNum, sql, parameters); } /** * Generate page Java Bean * * @param pageNum * @return PageBean contains all the information that the page needs to show */ public PageBean<T> generatePageBean(int pageNum) { PageBean<T> pageBean = new PageBean<T>(pageSize, pageNum, getTotalNum(), getData(pageNum)); return pageBean; } /** * 校验是否是SELECT查询语句 * * @param sql */ private static void validateSQL(String sql) { if (!(sql.toUpperCase().startsWith("SELECT"))) { throw new RuntimeException( "not supported operation, only query supported"); } } }
JPage的jar包和源文件在附录中, 至于文档中的几个问题大家有解决的办法没?
相关推荐
- 提高代码复用性:JPage作为一个独立的组件,可以在多个页面中重复使用。 - 易于维护:将分页逻辑封装在标签库中,减少了JSP页面的复杂性,方便维护。 - 用户体验:提供多种分页样式,满足不同设计需求,提升...
**jPage 分页——异步分页详解** 在网页开发中,当数据量过大时,一次性加载所有数据可能会导致页面加载速度慢,用户体验下降。为了解决这个问题,分页技术应运而生。jPage 是一个流行的 JavaScript 分页插件,它...
JPage组件是一个在IT行业中广泛使用的前端开发框架,主要用于构建高效、可维护的网页应用。这个框架通过提供一系列预封装的UI组件和便捷的数据绑定机制,帮助开发者快速地搭建交互丰富的用户界面。JPage组件下载通常...
2. **初始化分页组件**:通过jQuery选择器找到目标元素,并调用`jPage`方法,传入配置对象。 3. **绑定数据加载事件**:在`onPageChange`事件中,利用Ajax或其他方式获取新页面的数据并更新页面内容。 4. **可选:...
jQuery.jpage.js是一款基于jQuery的轻量级分页插件,用于实现网页内容的分页显示,提升用户体验,尤其在处理大量数据时效果显著。它提供了动态加载、切换动画等多种功能,使得用户在浏览长列表时能流畅地进行翻页...
2、Jpage分页会默认读取web.config配置文件中,名为data的连接字符串。 ;uid=sa;pwd=1234;database=cndata" providerName="System.Data.SqlClient"/> 在后置代码.cs中,如下: protected void Page_Load...
**jQuery jPage 分页插件详解** 在网页开发中,数据量庞大的页面往往需要实现分页功能,以提高用户体验和加载速度。`jPage` 是一个基于 jQuery 的轻量级分页插件,专为解决这个问题而设计。版本 `v1.0.8` 提供了...
《jPage:jQuery AJAX表格动态分页实现详解》 在网页开发中,数据展示往往涉及到大量的数据处理,尤其是在用户交互时,如何优雅地处理大量数据的分页展示,成为了提升用户体验的关键。jPage是一款基于jQuery的插件...
通过配置项设置每页显示条数、分页样式等,可以快速创建分页组件。 3. **Vue/React/Angular等框架**:这些现代前端框架提供了更强大的状态管理和组件化功能。可以通过自定义组件实现分页逻辑,例如Vue的`v-for`指令...
然后,你需要创建一个HTML结构来承载分页组件,一般是一个包含页码的无序列表: ```html <ul id="pagination"></ul> ``` 接下来,使用jQuery和jPage初始化分页: ```javascript $(document).ready(function() { ...
6. **分页组件**: - **PagingAndSortingRepository**:Spring Data JPA提供的分页接口。 - **JPage**:MyBatis-Plus中的分页类,简化了分页操作。 - **PaginationInterceptor**:MyBatis的拦截器,可自动处理...
其次,jPage是另一个强大的jQuery分页插件,它提供了更多的自定义选项,包括分页样式、主题、动画效果等。jPage不仅支持静态数据分页,还能够与服务器端通过AJAX进行动态数据交互,实现无刷新的页面切换。开发者可以...
而`jpage`文件可能是插件的核心代码,包含了一些核心函数和配置选项,用于初始化和控制分页功能。 总的来说,“jQuery中jhpage分页的改进版”是针对网页数据分页需求的一个实用解决方案,它不仅实现了基本的分页...
这些文件包括了分页样式表(jpage.css)和jQuery库,它们是实现分页效果的基础。 接下来,我们需要编写JavaScript代码来处理分页逻辑。这里的关键代码包括初始化分页状态、绑定数据、以及处理分页按钮的点击事件: ...
但是他们插件的附属功能很多又不需要,而且没必要就为了这么一个功能区引用这个库,为速度考虑吧,当然你服务器好也行,但是... jpage.js 代码如下:/* 排序工具 by Funny ZAk <silenceacegmail> 2009-8-23 “调用方式
10. **JPage.cs**: 分页工具类,通常用于数据库查询结果的分页显示,帮助用户分批查看大量数据。在Web应用中,分页是提升用户体验的重要手段。 这些工具类的组合,为.NET开发者提供了全面而便捷的支持,涵盖了...