文档章节

一种分页解决方案

猪刚烈
 猪刚烈
发布于 2014/10/12 11:40
字数 2217
阅读 13
收藏 0
点赞 0
评论 0

  开发MIS系统一个经常遇到的问题就是分页。这是一个经常出现而且没有太大变化的功能需求,我们完全可以编写一个可高度复用的解决方案。下面给出的就是一种基于Hibernate分页查询实现的一整套分页方案。在阐述这个方案之前,我想有必要就分页的相关问题做一个简单的阐述。

  一般来说分页有两种解决方法:一种是一次性查出所有符合条件的数据,只是在显示的时候选出一部分展示的页面上。这种做法的好处是实现起来简单,也不会频繁地访问的数据库,但是当查询出的数据量非常大而用户可能针对某一小部分数据感兴趣时,这种做法就变得十分低效了。另一种做法则是每次只查询出一页要显示的数据,每换一页,再重新查询一次。这种做法的好处是不会一次性读出所有数据,查询速度和查询效率都很高,但不足之处是需要频繁地访问数据库(事实上,由于连接池技术的存在这种频繁的访问也未必会真得会为数据库带来多大的负担)。应该说这两种方法各有优劣,要根据实际情况进行取舍。我个人更倾向于第二种解决方法,本文提供的解决方案也是基于这种方法实现的。

一、DAO层的分页组件

  DAO层的分页组件主要实现最低层的直接与数据库进行交互的分布查询功能。幸好Hibernate的API直接提供了对分页查询的支持:它们就是Query的setFirstResult方法(用于设置跳过的纪录数)和setMaxResults(用于设置一次要查出的纪录数)。一般来说分页是一个公共的需求,所以我们把分页方法写到BaseDAO里,代码如下:

public   class  BaseDAO
{
    
/**
     * description: 根据查询字符串进行分页查询,分页的位置由skipedRows和showedRows确定
     * 
     * 
@param  queryString
     *            查询字符串
     * 
@param  skipedRows
     *            路过的纪录数
     * 
@param  showedRows
     *            要显示的纪录数
     * 
@return  分布查询的结果
     
*/
    
public  List PagingQueryByHql(String queryString,  int  skipedRows,
            
int  showedRows)
    {
        List result 
=   null ;
        Session session 
=  HibernateSessionFactory.getCurrentSession();
        Query queryObject 
=  session.createQuery(queryString).setFirstResult(
                skipedRows).setMaxResults(showedRows);
        result 
=  queryObject.list();
        session.close();
        
return  result;
    }

    
/**
     * description:根据查询字符串查询纪录总数。关于这里使用的实现方法有待进一步斟酌
     * 
     * 
@param  queryString
     *            查询字符串
     * 
@return  纪录总数
     
*/
    
public   int  getQueryResultTotal(String queryString)
    {
        StringBuffer queryTotalString 
=   new  StringBuffer( " select count(*)  " );
        queryTotalString.append(queryString.substring(queryString
                .indexOf(
" from " )));
        Session session 
=  HibernateSessionFactory.getCurrentSession();
        Integer total 
=  (Integer) session.createQuery(
                queryTotalString.toString()).uniqueResult();
        
return  total.intValue();
    }
}

  按照B/S开发的三层架构,我们的分页功能也被分散到DAO层、业务层和Web层这三个层次,每一层关注于特定的实现,并向上一层提供服务。下面就分这三个层次来逐步展示我们的解决方案。

  二、业务层的分页组件

  业务层的分页组件只是简单调用DAO组件的相关方法即可,它的代码如下,其中ParticularManager是服务层定义的某个接口,ParticularManagerImpl是它的一个实现类。

public   class  ParticularManagerImpl  implements  ParticularManager
{

    
private  ParticularDAO;  //  一个特定DAO继承自BaseDAO,它由spring依赖注入

    
/**
     * description: 获取查询结果,调用ParticularDAO的PagingQueryByHql方法实现分页查询业务
     * 
     * 
@param  qo
     *            查询对象,携带查询关键字
     * 
@param  skipedRows
     *            路过的纪录数
     * 
@param  showedRows
     *            要显示的纪录数
     * 
@return  分页查询的结果
     
*/
    
public  List query(QO qo, int  skipedRows, int  showedRows)
    {
        
//  ......
    }

    
/**
     * description:获取查询结果的记录总数,调用ParticularDAO的getQueryResultTotal方法实现分页查询业务
     * 
     * 
@param  qo
     *            查询对象,携带查询关键字
     
*/
    
public   int  getRecordCount(QO qo)
    { 
        
//  ......
    }
}

三、Web层的分页组件

Web层的分页组件包含一个 ParticularForm,和一个ParticularAction。它们的代码分别如下:

public   class  ParticularForm  extends  ActionForm
{
    
private   static   final   long  serialVersionUID  =   1L ;
    
    
private  QO qo;     // 查询对象qo
    
    
private  List queryResult; //  查询结果
    
    
private  String footer = "" // 页角字符串
    
    
private   int  targetPage = 1 ;  // 目标页
    
    
private   int  pageSize = 20 ;  // 页面大小
    
    
public   void  reset(ActionMapping mapping, HttpServletRequest request)
    {
        qo
= new  QO();
        queryResult
= null ;    
    }
    
    
/*
     * 以下为各字段的getter和setter
     
*/
    
public  QO getQo()
    {
        
return  qo;
    }
    
public   void  setQo(QO qo)
    {
        
this .qo  =  qo;
    }
    
public  List getQueryResult()
    {
        
return  queryResult;
    }
    
public   void  setQueryResult(List queryResult)
    {
        
this .queryResult  =  queryResult;
    }

    
public  String getFooter()
    {
        
return  footer;
    }

    
public   void  setFooter(String footer)
    {
        
this .footer  =  footer;
    }

    
public   int  getPageSize()
    {
        
return  pageSize;
    }

    
public   void  setPageSize( int  pageSize)
    {
        
this .pageSize  =  pageSize;
    }

    
public   int  getTargetPage()
    {
        
return  targetPage;
    }

    
public   void  setTargetPage( int  targetPage)
    {
        
this .targetPage  =  targetPage;
    }

}

 

public   class  ParticularAction  extends  BaseAction
{

    
private  PagingUtil pagingUtil = new  PagingUtil();  // 分页工具类

    
/**
     * 查询信息
     * 
@param  mapping
     * 
@param  form
     * 
@param  request
     * 
@param  response
     * 
@return
     
*/
    
public  ActionForward query(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
    {
        ParticularForm particularForm
= (ParticularForm)form;
        ParticularManager particularManager 
=  (ParticularManager) ServiceLocator.getService( " particularManager " );
        StxxQueryQO qo
= stxxQueryForm.getQo();
        
if (qo == null )
            
return   null ;    
        
// 查询字符串    
        StringBuffer queryString = new  StringBuffer( " from BaseStxxbPO as st where 1=1 " );
        
// 从qo中取出数据,组织查询字符串....
        
// ......        
        pagingUtil.setRecordTotal(particularManager.getQueryResultTotal(queryString.toString()));    
        pagingUtil.setPageSize(particularForm.getPageSize());
        pagingUtil.setCurrentPage(particularForm.getTargetPage());
        List result
= particularManager.pagingQueryByHql(queryString.toString(),pagingUtil.getSkippedRecordTotal(),                                 pagingUtil.getPageSize());
        particularForm.setQueryResult(result);    
// 将查询结果赋给ActionForm
        particularForm.setFooter(pagingUtil.createSnippet());
        
return  mapping.findForward( " QueryList " ); // 转向列表页面    
    }
}

四、分页工具类

上面的类中用到了一个PagingUtil类,这是这个分页方案的又一个核心。它的代码如下:

/**
 * Description: 分页工具类,实现数据分页显示
 
*/
public   class  PagingUtil
{

    
private   int  recordTotal;  //  记录总数

    
private   int  pageSize;  //  每页显示记录数

    
private   int  pageTotal;  //  总页数

    
private   int  currentPage;  //  当前页数

    
private   boolean  hasPreviousPage;  //  是否有上一页

    
private   boolean  hasNextPage;  //  是否有下一页

    
/**
     * 默认构造器,将页面大小初始化为20
     
*/
    
public  PagingUtil()
    {
        
this .recordTotal  =   0 ;
        
this .pageSize  =   20 ;
        
this .pageTotal  =   1 ;
        
this .currentPage  =   1 ;
        
this .hasPreviousPage  =   false ;
        
this .hasNextPage  =   false ;
    }

    
/**
     * 根据记录总数和页面大小进行初始化
     * 
     * 
@param  recordTotal
     *            记录总数
     * 
@param  pageSize
     *            页面大小
     
*/
    
public  PagingUtil( int  recordTotal,  int  pageSize)
    {
        
this .recordTotal  =  recordTotal;
        
this .pageSize  =  pageSize;
        
this .currentPage  =   1 ;
        
this .resetPageTotal();
        
this .refresh();
    }

    
/**
     * 获取已经跳过的记录总数
     * 
     * 
@return  int 跳过的记录总数
     
*/
    
public   int  getSkippedRecordTotal()
    {
        
return  (currentPage  -   1 *  pageSize;
    }

    
/**
     * 刷新页面状态
     
*/
    
public   void  refresh()
    {
        
if  (pageTotal  <=   1 )
        {
            hasPreviousPage 
=   false ;
            hasNextPage 
=   false ;
        }
        
else   if  (currentPage  ==   1 )
        {
            hasPreviousPage 
=   false ;
            hasNextPage 
=   true ;
        }
        
else   if  (currentPage  ==  pageTotal)
        {
            hasPreviousPage 
=   true ;
            hasNextPage 
=   false ;
        }
        
else
        {
            hasPreviousPage 
=   true ;
            hasNextPage 
=   true ;
        }
    }

    
/**
     * 生成页面上实现分页导航功能的JSP页面片断,以字符串形式记录.
     * 
     * 
@return  String 其内容表示实现分页导航功能的JSP页面片断,该字符串使用<bean:write>标签写到页面上时必须放置在一个form里
     
*/
    
public  String createSnippet()
    {
        StringBuffer snippet 
=   new  StringBuffer( "" );

        
if  (hasPreviousPage)
        {
            snippet.append(
" <INPUT type=submit class='btn' value=首页 name=firs onclick='this.form.targetPage.value=1'> " );
            snippet.append(
" <INPUT type=submit class='btn'  value=上页 name=prev onclick='this.form.targetPage.value= " +  (currentPage  -   1 +   " '> " );
        }
        
else
        {
            snippet.append(
" <INPUT type=submit class='btn' value=首页 name=firs disabled> " );
            snippet.append(
" <INPUT type=submit class='btn'  value=上页 name=prev disabled> " );
        }
        
        
if  (hasNextPage)
        {
            snippet.append(
" <INPUT type=submit  class='btn' value=下页 name=next onclick='this.form.targetPage.value= " +  (currentPage  +   1 +   " '> " );
            snippet.append(
" <INPUT type=submit  class='btn' value=末页 name=last onclick='this.form.targetPage.value= " +  pageTotal  +   " '> " );
        }
        
else
        {
            snippet.append(
" <INPUT type=submit class='btn'  value=下页 name=next disabled> " );
            snippet.append(
" <INPUT type=submit class='btn'  value=末页 name=last disabled> " );
        }

        snippet.append(
"  共 "   +  recordTotal  +   " 条记录 " );
        snippet.append(
"   每页<SELECT size=1  name=pageSize onchange='this.form.targetPage.value=1;this.form.pageSize.value=this.value;this.form.submit();'> " );

        
if  (pageSize  ==   5 )
        {
            snippet.append(
" <OPTION value=5 selected>5</OPTION> " );
        }
        
else
        {
            snippet.append(
" <OPTION value=5>5</OPTION> " );
        }

        
if  (pageSize  ==   10 )
        {
            snippet.append(
" <OPTION value=10 selected>10</OPTION> " );
        }
        
else
        {
            snippet.append(
" <OPTION value=10>10</OPTION> " );
        }
        
if  (pageSize  ==   20 )
        {
            snippet.append(
" <OPTION value=20 selected>20</OPTION> " );
        }
        
else
        {
            snippet.append(
" <OPTION value=20>20</OPTION> " );
        }
        
if  (pageSize  ==   50 )
        {
            snippet.append(
" <OPTION value=50 selected>50</OPTION> " );
        }
        
else
        {
            snippet.append(
" <OPTION value=50>50</OPTION> " );
        }
        
if  (pageSize  ==   100 )
        {
            snippet.append(
" <OPTION value=100 selected>100</OPTION> " );
        }
        
else
        {
            snippet.append(
" <OPTION value=100>100</OPTION> " );
        }
        snippet.append(
" </SELECT> " );
        snippet.append(
" 条 分 "   +  pageTotal  +   " 页显示 转到 " );
        snippet.append(
" <SELECT size=1 name=Pagelist onchange='this.form.targetPage.value=this.value;this.form.submit();'> " );
        
for  ( int  i  =   1 ; i  <  pageTotal  +   1 ; i ++ )
        {
            
if  (i  ==  currentPage)
            {
                snippet.append(
" <OPTION value= "   +  i  +   "  selected> "   +  i
                        
+   " </OPTION> " );
            }
            
else
            {
                snippet.append(
" <OPTION value= "   +  i  +   " > "   +  i  +   " </OPTION> " );
            }
        }
        snippet.append(
" </SELECT>页 " );
        snippet.append(
" <INPUT type=hidden  value= "   +  currentPage
                
+   "  name="targetPage" >  " );
        snippet.append(
" <INPUT type=hidden  value= "   +  pageSize
                
+   "  name="pageSize">  " );
        
return  snippet.toString();
    }

    
/**  以下为getter和setter方法 *  */

    
/**
     * 获取记录总数
     * 
     * 
@return  int 总行数
     
*/
    
public   int  getRecordTotal()
    {
        
return  recordTotal;
    }

    
/**  
     * 设置记录总数,同时利用recordTotal和pageSize计算出pageTotal。
     * 
     * 
@param  recordTotal 记录总数
     
*/
    
public   void  setRecordTotal( int  recordTotal)
    {
        
this .recordTotal  =  recordTotal;
        resetPageTotal();
        refresh();
    }

    
/**
     * 计算总页数
     * 
     * 
@return  int 总页面数
     
*/
    
public   int  getPageTotal()
    {
        
return  pageTotal;
    }

    
/**
     * 重置页面总数
     
*/
    
protected   void  resetPageTotal()
    {
        
if  (pageSize  >=   1 )
        {
            pageTotal 
=  (recordTotal  +  pageSize  -   1 /  pageSize;
        }
        
else
        {
            pageTotal 
=   1 ;
        }
    }

    
/**
     * 获取每页显示记录数
     * 
     * 
@return  int 每页显示的记录数
     
*/
    
public   int  getPageSize()
    {
        
return  pageSize;
    }

    
/**
     * 
     * 设置每页显示记录数
     * 
     * 
@param  pageSize
     *            每页显示记录数
     
*/
    
public   void  setPageSize( int  pageSize)
    {
        
this .pageSize  =  pageSize;
        resetPageTotal();
        refresh();
    }

    
/**
     * 获取当前页数
     * 
     * 
@return  int 当前的页数
     
*/
    
public   int  getCurrentPage()
    {
        
return  currentPage;
    }

    
/**
     * 设置当前页数
     * 
     * 
@return  int 当前的页数
     
*/
    
public   void  setCurrentPage( int  currentPage)
    {
        
if (currentPage >= 1 && currentPage <= pageTotal)
        {
            
this .currentPage  =  currentPage;
            refresh();
        }
    }

    
/**
     * 获取是否有下一页的状态信息
     * 
     * 
@return  blooean 是否有下一页
     
*/
    
public   boolean  isHasNextPage()
    {
        
return  hasNextPage;
    }

    
/**
     * 获取是否有上一页的状态信息
     * 
     * 
@return  blooean 是否有上一页
     
*/
    
public   boolean  isHasPreviousPage()
    {
        
return  hasPreviousPage;
    }
}

  需要特别说明的是createSnippet方法,它的作用就是根据当前页面情况动态的组织要写入到页面上的分页页面片段。而我们在页面上的工作就会因此而变得轻松了许多,因为这样一来,我们在页面上唯一要做的就是写下这一样一个语句:<bean:write name="particularForm" property="footer"/>

本文转载自:http://blog.csdn.net/bluishglc/article/details/1715832

共有 人打赏支持
猪刚烈
粉丝 22
博文 708
码字总数 110
作品 1
海淀
程序员
DataGrid连接Access的快速分页法(1)——需求与现状

作者:黎波 一、需求分析 DataGrid是一个功能强大的ASP.NET Web服务器端控件,它除了能够按各种方式格式化显示数据,还可以对数据进行动态的排序、编辑和分页。大大减轻了广大Web程序员的工作...

晨曦之光 ⋅ 2012/03/09 ⋅ 0

memcached页面级缓存存在的问题

memcached实现了页面级缓存,根据查询条件跟分页参数生成唯一key,无规律的,缓存页面数据 存在问题:由于根据查询条件缓存与分页查询缓存的key值是根据查询条件得到的哈希值,没有规矩,导致...

翊骷 ⋅ 2014/07/31 ⋅ 0

数据库逆向框架代码生成工具:MyBatis Generator的使用

MyBatis Generator的详细介绍http://mybatis.github.io/generator/index.html MyBatis Generator With Maven http://mybatis.github.io/generator/running/runningWithMaven.html 1前言 前段......

蛙牛 ⋅ 2014/02/16 ⋅ 28

几种web报表打印方案的比较

web报表打印通常是系统的辅助部分,但是又必须解决,否则项目完成不了。下面来分析分析目前的几种常见的web报表打印方案。 一、 浏览器的菜单打印功能 这是最简单的,只需点击页面上的打印菜...

长平狐 ⋅ 2012/10/11 ⋅ 0

用memcached做实时分页缓存

用memcached做分页缓存,可能很多人会觉得麻烦而不用。因为在增加、修改、删除的过程中,你不知道会影响到哪些数据,而如果把所有分页相关的数据缓存都删除并重新生成一遍,实现又很麻烦,甚...

BearCatYN ⋅ 2015/01/30 ⋅ 1

报表性能优化方案之行引擎执行层式报表

问题描述 这样一种报表:报表形式非常简单,只是简单的单数据源明细报表,但是数据量非常大,百万、千万甚至更多。报表取数及计算时间相当长。 希望能够提高报表展示速度,对于用户来说,查询...

九月你好123 ⋅ 2015/09/16 ⋅ 0

web打印的几种实现方法(转载)

web打印的几种实现方法(转载) web打印,web打印控件的三种实现方法 做管理系统的时候,打印一直是个棘手的问题,做B/S的系统这个问题就更加突出了!下面举出三种常用的web打印处理方式 1、...

什么是程序员 ⋅ 2015/09/18 ⋅ 0

数据库查询压力大、web端容易内存溢出——解决方案、问题总结

背景 近期,由于公司以前的外包项目限制了使用用户数,在各种反编译和外包公司商量之后,还是决定重写个web端,直接读写数据库。但实现过程中,发现财务对采购入库销售出库的数据展示和操作量...

涛洛洛 ⋅ 2016/11/28 ⋅ 0

思考了几年的数据权限终于有解了

前面写了博客聊聊数据权限哪些事儿,实际上在写那篇文章的时候,思路就已经思考好了,然后就是代码实现了。相对于解决方案,代码简直就太容易了。 首先说说功能特性 数据行权限--让不同的人看...

悠悠然然 ⋅ 2016/04/06 ⋅ 32

mysql 怎么实现随机查询并分页,不重复查询

需求: 企业应用中,一般数据量不是很特别多,同时大多在局域网内,性能不会有问题,一般不会分页处理,随机排序数据一般不会有问题 web网站中,一般都用到分页处理,目前数据量比较少,考虑...

solar.xie ⋅ 2013/05/15 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

20.zip压缩 tar打包 打包并压缩

6月25日任务 6.5 zip压缩工具 6.6 tar打包 6.7 打包并压缩 6.5 zip压缩工具: zip支持压缩目录 zip压缩完之后原来的文件不删除 不同的文件内容其实压缩的效果不一样 文件内有很多重复的用xz压...

王鑫linux ⋅ 15分钟前 ⋅ 0

double类型数据保留四位小数的另一种思路

来源:透析公式处理,有时候数据有很长的小数位,有的时候由在四位以内,如果用一般的处理方法,那么不足四位的小树会补充0到第四位,这样子有点画蛇添足的感觉,不太好看。所以要根据小数的...

young_chen ⋅ 21分钟前 ⋅ 0

Python 优化 回溯下降算法

使用sympy构造表达式,实现回溯下降算法 画出函数图像,先使用暴力搜索,找到最小值约为2.5左右 然后选定初始点,开始进行回溯搜索,下降方向为负梯度方向 下降的误差与步数大致呈现下面的状...

阿豪boy ⋅ 26分钟前 ⋅ 0

Django配置163邮箱出现 authentication failed(535)错误解决方法

最近用Django写某网站,当配置163邮箱设置完成后,出现535错误即:smtplib.SMTPAuthenticationError: (535, b'Error: authentication failed') Django初始配置邮箱设置 EMAIL_HOST = "smtp.1...

陈墨轩_CJX ⋅ 27分钟前 ⋅ 0

用接口模拟可伸缩枚举(34)

1、枚举的可伸缩性最后证明都不是什么好点子 扩展类型的元素是基本类型实例,基本类型的实例却不是扩展类型的元素,很混乱 目前还没有很好的方法来枚举基本类型的所有元素,及其扩展 可伸缩性...

职业搬砖20年 ⋅ 31分钟前 ⋅ 0

Ubuntu18.04 IDEA快捷键无法使用

IDEA默认的回退到上一视图的快捷键是Ctrl + Alt + Left,在ubuntu中这个快捷键被占用了,在16.04中可以在界面中取消这个快捷键,但是18.04就看不到了,可以使用以下命令解决 gsettings set ...

Iceberg_XTY ⋅ 35分钟前 ⋅ 0

如何解决s权限位引发postfix及crontab异常

一、问题现象 业务反馈某台应用服务器,普通用户使用mutt程序发送邮件时,提示“postdrop warning: mail_queue_enter: create file maildrop/713410.6065: Permission denied”,而且普通用法...

问题终结者 ⋅ 47分钟前 ⋅ 0

Unable to load database on disk

由于磁盘空间满了以后,导致zookeeper异常退出,清理磁盘空间后,zk启动报错,信息如下: 2018-06-25 17:18:46,904 INFO org.apache.zookeeper.server.quorum.QuorumPeerConfig: Reading co...

刀锋 ⋅ 今天 ⋅ 0

css3 box-sizing:border-box 实现div一行多列

<!DOCTYPE html><html><head><style> div.container{ background:green; padding:10px 10px;}div.box{box-sizing:border-box;-moz-box-sizing:border-box; /* Fir......

qimh ⋅ 今天 ⋅ 0

Homebrew简介和基本使用

一、Homebrew是什么 Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径...

说回答 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部