开发MIS系统一个经常遇到的问题就是分页。这是一个经常出现而且没有太大变化的功能需求,我们完全可以编写一个可高度复用的解决方案。下面给出的就是一种基于Hibernate分页查询实现的一整套分页方案。在阐述这个方案之前,我想有必要就分页的相关问题做一个简单的阐述。
一般来说分页有两种解决方法:一种是一次性查出所有符合条件的数据,只是在显示的时候选出一部分展示的页面上。这种做法的好处是实现起来简单,也不会频繁地访问的数据库,但是当查询出的数据量非常大而用户可能针对某一小部分数据感兴趣时,这种做法就变得十分低效了。另一种做法则是每次只查询出一页要显示的数据,每换一页,再重新查询一次。这种做法的好处是不会一次性读出所有数据,查询速度和查询效率都很高,但不足之处是需要频繁地访问数据库(事实上,由于连接池技术的存在这种频繁的访问也未必会真得会为数据库带来多大的负担)。应该说这两种方法各有优劣,要根据实际情况进行取舍。我个人更倾向于第二种解决方法,本文提供的解决方案也是基于这种方法实现的。
一、DAO层的分页组件
DAO层的分页组件主要实现最低层的直接与数据库进行交互的分布查询功能。幸好Hibernate的API直接提供了对分页查询的支持:它们就是Query的setFirstResult方法(用于设置跳过的纪录数)和setMaxResults(用于设置一次要查出的纪录数)。一般来说分页是一个公共的需求,所以我们把分页方法写到BaseDAO里,代码如下:
publicclassBaseDAO
{
/**
*description:根据查询字符串进行分页查询,分页的位置由skipedRows和showedRows确定
*
*@paramqueryString
*查询字符串
*@paramskipedRows
*路过的纪录数
*@paramshowedRows
*要显示的纪录数
*@return分布查询的结果
*/
publicListPagingQueryByHql(StringqueryString,intskipedRows,
intshowedRows)
{
Listresult=null;
Sessionsession=HibernateSessionFactory.getCurrentSession();
QueryqueryObject=session.createQuery(queryString).setFirstResult(
skipedRows).setMaxResults(showedRows);
result=queryObject.list();
session.close();
returnresult;
}
/**
*description:根据查询字符串查询纪录总数。关于这里使用的实现方法有待进一步斟酌
*
*@paramqueryString
*查询字符串
*@return纪录总数
*/
publicintgetQueryResultTotal(StringqueryString)
{
StringBufferqueryTotalString=newStringBuffer("selectcount(*)");
queryTotalString.append(queryString.substring(queryString
.indexOf("from")));
Sessionsession=HibernateSessionFactory.getCurrentSession();
Integertotal=(Integer)session.createQuery(
queryTotalString.toString()).uniqueResult();
returntotal.intValue();
}
}
按照B/S开发的三层架构,我们的分页功能也被分散到DAO层、业务层和Web层这三个层次,每一层关注于特定的实现,并向上一层提供服务。下面就分这三个层次来逐步展示我们的解决方案。
二、业务层的分页组件
业务层的分页组件只是简单调用DAO组件的相关方法即可,它的代码如下,其中ParticularManager是服务层定义的某个接口,ParticularManagerImpl是它的一个实现类。
publicclassParticularManagerImplimplementsParticularManager
{
privateParticularDAO;//一个特定DAO继承自BaseDAO,它由spring依赖注入
/**
*description:获取查询结果,调用ParticularDAO的PagingQueryByHql方法实现分页查询业务
*
*@paramqo
*查询对象,携带查询关键字
*@paramskipedRows
*路过的纪录数
*@paramshowedRows
*要显示的纪录数
*@return分页查询的结果
*/
publicListquery(QOqo,intskipedRows,intshowedRows)
{
//......
}
/**
*description:获取查询结果的记录总数,调用ParticularDAO的getQueryResultTotal方法实现分页查询业务
*
*@paramqo
*查询对象,携带查询关键字
*/
publicintgetRecordCount(QOqo)
{
//......
}
}
三、Web层的分页组件
Web层的分页组件包含一个ParticularForm,和一个ParticularAction。它们的代码分别如下:
publicclassParticularFormextendsActionForm
{
privatestaticfinallongserialVersionUID=1L;
privateQOqo;//查询对象qo
privateListqueryResult;//查询结果
privateStringfooter="";//页角字符串
privateinttargetPage=1; //目标页
privateintpageSize=20; //页面大小
publicvoidreset(ActionMappingmapping,HttpServletRequestrequest)
{
qo=newQO();
queryResult=null;
}
/*
*以下为各字段的getter和setter
*/
publicQOgetQo()
{
returnqo;
}
publicvoidsetQo(QOqo)
{
this.qo=qo;
}
publicListgetQueryResult()
{
returnqueryResult;
}
publicvoidsetQueryResult(ListqueryResult)
{
this.queryResult=queryResult;
}
publicStringgetFooter()
{
returnfooter;
}
publicvoidsetFooter(Stringfooter)
{
this.footer=footer;
}
publicintgetPageSize()
{
returnpageSize;
}
publicvoidsetPageSize(intpageSize)
{
this.pageSize=pageSize;
}
publicintgetTargetPage()
{
returntargetPage;
}
publicvoidsetTargetPage(inttargetPage)
{
this.targetPage=targetPage;
}
}
publicclassParticularActionextendsBaseAction
{
privatePagingUtilpagingUtil=newPagingUtil(); //分页工具类
/**
*查询信息
*@parammapping
*@paramform
*@paramrequest
*@paramresponse
*@return
*/
publicActionForwardquery(ActionMappingmapping,ActionFormform,
HttpServletRequestrequest,HttpServletResponseresponse)
{
ParticularFormparticularForm=(ParticularForm)form;
ParticularManagerparticularManager=(ParticularManager)ServiceLocator.getService("particularManager");
StxxQueryQOqo=stxxQueryForm.getQo();
if(qo==null)
returnnull;
//查询字符串
StringBufferqueryString=newStringBuffer("fromBaseStxxbPOasstwhere1=1");
//从qo中取出数据,组织查询字符串....
//......
pagingUtil.setRecordTotal(particularManager.getQueryResultTotal(queryString.toString()));
pagingUtil.setPageSize(particularForm.getPageSize());
pagingUtil.setCurrentPage(particularForm.getTargetPage());
Listresult=particularManager.pagingQueryByHql(queryString.toString(),pagingUtil.getSkippedRecordTotal(), pagingUtil.getPageSize());
particularForm.setQueryResult(result);//将查询结果赋给ActionForm
particularForm.setFooter(pagingUtil.createSnippet());
returnmapping.findForward("QueryList");//转向列表页面
}
}
四、分页工具类
上面的类中用到了一个PagingUtil类,这是这个分页方案的又一个核心。它的代码如下:
/**
*Description:分页工具类,实现数据分页显示
*/
publicclassPagingUtil
{
privateintrecordTotal;//记录总数
privateintpageSize;//每页显示记录数
privateintpageTotal;//总页数
privateintcurrentPage;//当前页数
privatebooleanhasPreviousPage;//是否有上一页
privatebooleanhasNextPage;//是否有下一页
/**
*默认构造器,将页面大小初始化为20
*/
publicPagingUtil()
{
this.recordTotal=0;
this.pageSize=20;
this.pageTotal=1;
this.currentPage=1;
this.hasPreviousPage=false;
this.hasNextPage=false;
}
/**
*根据记录总数和页面大小进行初始化
*
*@paramrecordTotal
*记录总数
*@parampageSize
*页面大小
*/
publicPagingUtil(intrecordTotal,intpageSize)
{
this.recordTotal=recordTotal;
this.pageSize=pageSize;
this.currentPage=1;
this.resetPageTotal();
this.refresh();
}
/**
*获取已经跳过的记录总数
*
*@returnint跳过的记录总数
*/
publicintgetSkippedRecordTotal()
{
return(currentPage-1)*pageSize;
}
/**
*刷新页面状态
*/
publicvoidrefresh()
{
if(pageTotal<=1)
{
hasPreviousPage=false;
hasNextPage=false;
}
elseif(currentPage==1)
{
hasPreviousPage=false;
hasNextPage=true;
}
elseif(currentPage==pageTotal)
{
hasPreviousPage=true;
hasNextPage=false;
}
else
{
hasPreviousPage=true;
hasNextPage=true;
}
}
/**
*生成页面上实现分页导航功能的JSP页面片断,以字符串形式记录.
*
*@returnString其内容表示实现分页导航功能的JSP页面片断,该字符串使用<bean:write>标签写到页面上时必须放置在一个form里
*/
publicStringcreateSnippet()
{
StringBuffersnippet=newStringBuffer("");
if(hasPreviousPage)
{
snippet.append("<INPUTtype=submitclass='btn'value=首页name=firsonclick='this.form.targetPage.value=1'>");
snippet.append("<INPUTtype=submitclass='btn'value=上页name=prevonclick='this.form.targetPage.value="+(currentPage-1)+"'>");
}
else
{
snippet.append("<INPUTtype=submitclass='btn'value=首页name=firsdisabled>");
snippet.append("<INPUTtype=submitclass='btn'value=上页name=prevdisabled>");
}
if(hasNextPage)
{
snippet.append("<INPUTtype=submitclass='btn'value=下页name=nextonclick='this.form.targetPage.value="+(currentPage+1)+"'>");
snippet.append("<INPUTtype=submitclass='btn'value=末页name=lastonclick='this.form.targetPage.value="+pageTotal+"'>");
}
else
{
snippet.append("<INPUTtype=submitclass='btn'value=下页name=nextdisabled>");
snippet.append("<INPUTtype=submitclass='btn'value=末页name=lastdisabled>");
}
snippet.append("共"+recordTotal+"条记录");
snippet.append("每页<SELECTsize=1name=pageSizeonchange='this.form.targetPage.value=1;this.form.pageSize.value=this.value;this.form.submit();'>");
if(pageSize==5)
{
snippet.append("<OPTIONvalue=5selected>5</OPTION>");
}
else
{
snippet.append("<OPTIONvalue=5>5</OPTION>");
}
if(pageSize==10)
{
snippet.append("<OPTIONvalue=10selected>10</OPTION>");
}
else
{
snippet.append("<OPTIONvalue=10>10</OPTION>");
}
if(pageSize==20)
{
snippet.append("<OPTIONvalue=20selected>20</OPTION>");
}
else
{
snippet.append("<OPTIONvalue=20>20</OPTION>");
}
if(pageSize==50)
{
snippet.append("<OPTIONvalue=50selected>50</OPTION>");
}
else
{
snippet.append("<OPTIONvalue=50>50</OPTION>");
}
if(pageSize==100)
{
snippet.append("<OPTIONvalue=100selected>100</OPTION>");
}
else
{
snippet.append("<OPTIONvalue=100>100</OPTION>");
}
snippet.append("</SELECT>");
snippet.append("条分"+pageTotal+"页显示转到");
snippet.append("<SELECTsize=1name=Pagelistonchange='this.form.targetPage.value=this.value;this.form.submit();'>");
for(inti=1;i<pageTotal+1;i++)
{
if(i==currentPage)
{
snippet.append("<OPTIONvalue="+i+"selected>"+i
+"</OPTION>");
}
else
{
snippet.append("<OPTIONvalue="+i+">"+i+"</OPTION>");
}
}
snippet.append("</SELECT>页");
snippet.append("<INPUTtype=hiddenvalue="+currentPage
+"name="targetPage">");
snippet.append("<INPUTtype=hiddenvalue="+pageSize
+"name="pageSize">");
returnsnippet.toString();
}
/**以下为getter和setter方法**/
/**
*获取记录总数
*
*@returnint总行数
*/
publicintgetRecordTotal()
{
returnrecordTotal;
}
/**
*设置记录总数,同时利用recordTotal和pageSize计算出pageTotal。
*
*@paramrecordTotal记录总数
*/
publicvoidsetRecordTotal(intrecordTotal)
{
this.recordTotal=recordTotal;
resetPageTotal();
refresh();
}
/**
*计算总页数
*
*@returnint总页面数
*/
publicintgetPageTotal()
{
returnpageTotal;
}
/**
*重置页面总数
*/
protectedvoidresetPageTotal()
{
if(pageSize>=1)
{
pageTotal=(recordTotal+pageSize-1)/pageSize;
}
else
{
pageTotal=1;
}
}
/**
*获取每页显示记录数
*
*@returnint每页显示的记录数
*/
publicintgetPageSize()
{
returnpageSize;
}
/**
*
*设置每页显示记录数
*
*@parampageSize
*每页显示记录数
*/
publicvoidsetPageSize(intpageSize)
{
this.pageSize=pageSize;
resetPageTotal();
refresh();
}
/**
*获取当前页数
*
*@returnint当前的页数
*/
publicintgetCurrentPage()
{
returncurrentPage;
}
/**
*设置当前页数
*
*@returnint当前的页数
*/
publicvoidsetCurrentPage(intcurrentPage)
{
if(currentPage>=1&¤tPage<=pageTotal)
{
this.currentPage=currentPage;
refresh();
}
}
/**
*获取是否有下一页的状态信息
*
*@returnblooean是否有下一页
*/
publicbooleanisHasNextPage()
{
returnhasNextPage;
}
/**
*获取是否有上一页的状态信息
*
*@returnblooean是否有上一页
*/
publicbooleanisHasPreviousPage()
{
returnhasPreviousPage;
}
}
需要特别说明的是createSnippet方法,它的作用就是根据当前页面情况动态的组织要写入到页面上的分页页面片段。而我们在页面上的工作就会因此而变得轻松了许多,因为这样一来,我们在页面上唯一要做的就是写下这一样一个语句:<bean:write name="particularForm" property="footer"/>
分享到:
相关推荐
提供了使用SpringMVC、Mybatis、Easy UI框架进行分页的一种解决方案
信息检索完成后, 是需要经过传输(从存储介质到应用程序)和相关计算(业务逻辑)的, 因此, 我们需要一种分段的信息检索机制来降低这种冗余. 分页应运而生. 2. 分页的发展 基本的分页程序, 将数据按照每页记录数(page...
有两种分页的解决方案,一种是第一次把所有的资料都查询出来,然后在每页中显示指定的材料:另一种是多次查询数据库,每次只获得本页的数 据。考虑到数据往往是大量甚至是海量的,如果一次性的获取,那么这些数据必 ...
一,最常见MYSQL最基本的分页方式: 代码如下:select * from content order by id desc limit 0, 10在中小数据...此时,我们可以通过2种方式:一,子查询的分页方式来提高分页效率,飘易用的SQL语句如下: 代码如下:SEL
1、 首先将AspNetPager.dll复制于应用程序下的bin目录,打开解决方案,引入dll文件 2、 在工具栏中添加控件,这样可以支持拖拽使用 3、 要使用AspNetPager 要为其设置最基本的属性 使用 SqlServer Northwind数据库的...
WPF ListView如何分页困扰了我很久,百度...经过笔者一晚上的摸索,得到了一种比较简单的解决方案,在此贡献给大家。由于笔者在CSDN上的积分一直很少,所以这次就收3分啦,大家不要嫌贵,多支持一下啊,相互学习鼓励
第二种解决方案,一张表的增删查改都在一个serlvet中通过带参数进行流程处理,比如:empservlet?action=add empservlet?action=update empservlet?action=del 那么:显示所有的直接是empservlet 判断是通过去...
随着数据库中信息量的增大$查询分页技术已成为一种常用技术* 文章从数据库连接与查询+分页显示和页面保存方法 三方面入手$提出了基于 012 技术的数据库查询分页技术的解决方案$并给出了关键代码
对于这个需求很多朋友都有自己的解决方案,比如使用Vector等集合类先保存取出的数据再分页。但这种方法的可用性很差,与JDBC本身的接口完全不同,对不同类型的字段的支持也不好。这里提供了一种与JDBC兼容性非常好的...
对于这个需求很多朋友都有自己的解决方案,比如使用Vector等集合类先保存取出的数据再分页。但这种方法的可用性很差,与JDBC本身的接口完全不同,对不同类型的字段的支持也不好。这里提供了一种与JDBC兼容性非常好的...
办公自动化系统的开发,...4.3 分页解决方案JavaBean的编写 5 视图层的设计与实现 5.1 公告管理视图层的设计 5.2 公文管理视图层的设计 6 控制层的设计与实现 6.1 公告管理控制层的设计 6.2 公文管理控制层的设计
然后,这个repo并不是要提供一个完整的滚动加载组件,而是,提供一种在数据量大的情况下,对列表的滚动加载进行优化的解决方案。 ps:我相信有很多人知道方法,但也应该有多余人还不了解。 思考 首先,为何要对数据...
一个示例应用程序,展示了SEMBAST的使用,这是针对单进程io应用程序的另一种NoSQL持久存储数据库解决方案。 如果您刚开始,将数据持久存储在Flutter中并不是最简单的体验之一。 如果您想超越仅是键值对的简单“首...
es 提供了3种方式来解决分页与遍历的问题: – from/size – scroll – search_afterForm/Size 最常见的分页方案 – from指明开始位置 – size 指明获取总数 scroll 遍历文档集的 api,以快照的方式来避免深度分页的...
防止出现换库的情况)、支持分库分表** 等等,这时候第一时间就想到了 [FreeSql](https://github.com/dotnetcore/FreeSql) ,FreeSql的架构设计非常好,每一种支持的数据库都有对应的Provider实现 做到行为一致,...
该项目可帮助您轻松地将这些功能添加到REST Controller中,而无需开发自定义解决方案。 该项目目前仅与Spring Data MongoDB应用程序兼容。 这里有一篇博客文章,我其中逐步描述了该库的实现。 入门 对于基于Maven的...
只需使用一种更好的解决方案,例如,这个 BubblePagerIndicator 视图分页器指示器视图可处理大量页面。 支持漂亮的缩放和过渡动画。 用法 将mavenCentral()添加到gradle文件中的存储库块中。 将implementation ...
备注:此项目仅提供基础的模板技术演示,旨在与广大技术员交流之用,也可视为静态模板技术的一种解决思路。 静态页生成的文件为:StaticMain.htm、StaticDetail_1.htm、StaticDetail_2.htm、StaticDetail_3.htm 此...
下面是我在学习ajax的过程中,实现的一个解决方案,不知道设计得怎么样,所以想发出来给大家参考下,恳请给予建议和指导,狗狗感激不尽!需求概述:需要将二维数据通过表格展现给客户端,用户可以事先选择每页显示的...