分页技术框架(Pager-taglib)学习二(SSH数据库分页)
一、Pager-taglib数据库分页前提
Pager-taglib分页标签也可以实现数据库分页,与页面分页不同的是需要给后台传两个参数,一个是pageNo(当前页数)或pageOffset(偏移量),另一个参数是pageSize(分页尺寸)。数据库分页的实质是根据页面传递过来的这两个参数从数据库中提取部分记录(而不是所有记录),然后由页面呈现这些数据。因每次只装载一部分数据,因而占据少量内存,适合大量数据查询。
二、SSH数据库分页示例(通用设计)
1、准备工作
首先下载pager-taglib-2.0.war
,地址http://jsptags.com/tags/navigation/pager/download.jsp,然后将lib下面的包lib下的jar包放到自己项目的lib下面。
生成测试表:t_person,其中实体类Person类有id,name,phone属性,具体在hibernate配置文件中的配置略..。
2、分页实体类设计
创建PagerModel类用于存放从数据库查询的所有person,和总记录数。
public class PagerModel
{
private int
total;//总记录数
private List
datas;//所有用户集合
...get和set方法
}
3.编写通用数据访问组件
import org.hibernate.Query;
import
org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class BaseHibernateDao extends
HibernateDaoSupport
{
//根据多个查询条件params获取指定页的记录集。offset:页面记录的开始位置,即偏移量
//pageSize:页面尺寸
public
PagerModel searchPagerModel(String hql,Object[] params,int
offset,int pageSize)
{
//获取查询记录总数hql语句
String
countHql = getCountQuery(hql);
Query query
=getSession().createQuery(countHql);
if(params!=null
&&
params.length>0)
{
for(int
i=0;i<params.length;i++)
{
query.setParameter(i, params[i]);
}
}
//获取总记录的条数
int total = ((Long)
query.uniqueResult()).intValue();
//获取当前页面的结果集
query =
super.getSession().createQuery(hql);
if(params!=null
&&
params.length>0)
{
for(int
i=0;i<params.length;i++)
{
query.setParameter(i, params[i]);
}
}
query.setFirstResult(offset);
query.setMaxResults(pageSize);
PagerModel pagerModel = new PagerModel();
//设置当前页面记录和总记录数
pagerModel.setDatas(query.list());
pagerModel.setTotal(total);
return pagerModel;
}
//重载方法:根据单个查询条件param获取指定页的记录集。
public
PagerModel searPagerModel(String hql ,Object param)
{
return
this.searchPagerModel(hql, new Object[]{param},
SystemContext.getOffset(), SystemContext.getPagesize());
}
public
PagerModel searchPagerModel(String hql)
{
return
this.searchPagerModel(hql, null, SystemContext.getOffset(),
SystemContext.getPagesize());
}
public
PagerModel searchPagerModel(String hql ,Object[] params)
{
return
this.searchPagerModel(hql, params, SystemContext.getOffset(),
SystemContext.getPagesize());
}
//根据hql语句,获得查找总记录数的hql语句
private
String getCountQuery(String hql)
{
int index
=hql.indexOf("from");
if(index
!=-1)
{
return
"select count(*)"+hql.substring(index);
}
throw new
RuntimeException("无效的HQL查询语句");
}
}
有了BaseHibernateDao组件,我们的PersonServiceImpl改写为:
public class PersonServiceImpl
extends
BaseHibernateDao implements PersonService
{
public PagerModel searchPerson(int offset,int pageSize){
String hql ="from Person as person order by person.id desc";
return super.searchPagerModel(hql,null,offset,pageSize);
}
}
4.编写过滤器及其相关组件(可以将它们放在同一个包下)
4.1、编写SystemContext组件
将系统启动时显示起始记录数和每页显示行数设置在ThreadLocal中。
public class SystemContext
{
//用于query.setFirstResult(offset)
private static ThreadLocal < Integer
> offset = new ThreadLocal < Integer
> ();
//pagesize每页显示的行数用于
//query.setMaxResults(pagesize);
private static ThreadLocal < Integer
> pagesize = new ThreadLocal <
Integer > ();
-----ThreadLocal线程局部变量,
就是为每一个使用该变量的线程都提供一个变量值的副本,每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突,
也就是每一个用户都可以改变自己的每页显示的行数而不会影响到其他人
public
static void setOffset(int offsetvalue)
{
offset.set(offsetvalue);
}
public
static int getOffset()
{
Integer ov =
offset.get();
if (ov ==
null) {
return
0;
}
return
ov;
}
public
static void setPagesize(int pagesizevalue)
{
pagesize.set(pagesizevalue);
}
public
static int getPagesize()
{
Integer ps =
pagesize.get();
if (ps ==
null) {
return
Integer.MAX_VALUE; //将pagesize设置为无穷大,也就是显示在一页上
}
return
ps;
}
public
static void removeOffset()
{ //清除为每个用户分配的副本
offset.remove();
}
public
static void removePagesize() {
pagesize.remove();
}
}
4.2Env组件(加载配置文件)
public class Env extends
Properties
{
private
static Env env;
public
static Env getInstance()
{
makeInstance();
return
env;
}
//以单例模式创建对象
private
static synchronized void makeInstance()
{
if (env
==null) {
env = new
Env();
}
}
private
Env()
{
//该属性文件含有pageModel_pageSize属性(分页尺寸)
InputStream
inStream=getClass().getResourceAsStream("/env.Properties");
try
{
super.load(inStream);
}
catch
(IOException)
{
e.printStackTrace();
}
}
}
4.3、PagerFilter组件
用于设置加载用户自定义的pagesize和offset
public class PagerFilter
implements Filter
{
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException,
ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)
request;
//设置分页参数:偏移量和页面尺寸
SystemContext.setOffset(getOffset(httpRequest));
SystemContext.setPagesize(getPagesize());
try {
chain.doFilter(request, response);
} finally
{
SystemContext.removeOffset();
SystemContext.removePagesize();
}
}
private int getOffset(HttpServletRequest
httpRequest) {
int offset =
0;
try {
offset =
Integer.parseInt(httpRequest.getParameter("pager.offset"));
//得到标签自己计算出的pager.offset
} catch
(Exception ignore) {}
return
offset;
}
//获取每页显示的行数
private int getPagesize()
{
int pageSize = 0;
try
{
pageSize=Integer.parseInt(Env.getInstance().getProperty("pageModel_pageSize"))
} catch (NumberFormatException e) {
pageSize = Integer.MAX_VALUE;
}
return pageSize;
}
}
在web.xml中配置:
<filter>
<filter-name>pagerfilter</filter-name>
<filter-class>com.test.filter.PagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>pagerfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5、控制器PersonAction核心代码(部分)
public
String getPersons()
{
....省略得到session定义
PagerModel pm
=PersonService.searchPerson(pageOffset,pageSize);
session.setAttribute("pm",pm);
return "success";//sucess配置转向的是pager_index.jsp页面
}
说明:我们已经在该actin中定了了pageOffset的get/set方法;pageSize是我们写死的(如pageSize=4),并且已经提供了get方法(供EL表达式调用)。
6、配置分页风格组件:pager_conn.jsp(重点)
<%@ taglib prefix="pg"
uri="http://jsptags.com/tags/navigation/pager"
%>
<%@ taglib
prefix="c"
uri="http://java.sun.com/jsp/jstl/core"
%>
<pg:pager
url="personAction!getPerson"
items="${pm.total}"
maxPageItems = "${pageSize}"
maxIndexPages="3" export="pageOffset,currentPageNumber=pageNumber"
isOffset="false"
index="half-full"
>
<%--
是否向后台传参:personAction!getPerson?method=${parm}
<c:if test="${not empty param
}">
<pg:param name="method" value="${param
}"/>
</c:if>
--%>
<pg:first>
<a
href="${pageUrl}">首页</a>
</pg:first>
<pg:prev>
<a href="${pageUrl
}">上一页</a>
</pg:prev>
<pg:pages>
<c:choose>
<c:when test="${currentPageNumber eq
pageNumber}">
<font color="red">${pageNumber
}</font>
</c:when>
<c:otherwise>
<a href="${pageUrl }">${pageNumber
}</a>
</c:otherwise>
</c:choose>
</pg:pages>
<pg:next>
<a href="${pageUrl
}">下一页</a>
</pg:next>
<pg:last>
<a href="${pageUrl
}">尾页</a>
</pg:last>
</pg:pager>
注意:数据库分页和页面分页不用的是:页面分页分页内容和分页风格是耦合在一起的;数据库分页分页内容和分页风格是对立的(即pg:paper根元素中不会含有分页内容)
7、分页内容文件pager_index.jsp
<%@ taglib
prefix="c"
uri="http://java.sun.com/jsp/jstl/core"
%>
<table width="778" border="0" cellPadding="0"
cellSpacing="1"
bgcolor="#6386d6">
<tr
bgcolor="#EFF3F7">
<TD
align="center">ID</TD>
<TD
align="center">姓名</TD>
<TD
align="center">电话</TD>
</tr>
<c:if test="${!empty
pm.datas}">
<c:forEach items="${pm.datas}"
var="person">
<tr
bgcolor="#EFF3F7">
<td align="center">${person.id
}</td>
<td align="center">${person.name
}</td>
<td
align="center">${person.phone}</td>
</tr>
</c:forEach>
</c:if>
<c:if test="${empty
pm.datas}">
<tr>
<td colspan="5" align="center"
bgcolor="#EFF3F7">
没有找到相应的记录
</td>
</tr>
</c:if>
</table>
<jsp:include
page="/WEB-INF/jsp/pager_conn.jsp"
flush="true"></jsp:include>
说明:
上面使用的动态包含,但前提是:要包含的风格文件pager_conn.jsp能够独立编译(即一定要导入6中的两个jsp页面指令);如何使用静态包含也可以,前提是:两页面合并后一定要能正确编译(即合并后一定要有6中的两个jsp页面指令,合并前这两个指令分散在哪个页面无所谓)
三、总结
pager便签库有两种自行计算分页参数(pageOffset)的方式:一种是根据pg:pager的items属性(设置总记录数)
和maxPageItems属性(设置一页的记录数),适用于数据库分页;另一种是根据pg:pager的maxPageItems属性(设置页尺寸)和pg:item标签,这种适合页面分页。
风格