已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
在JSF中实现分页(一) 对于大多数Web应用,分页都是必不可少的功能,当然在JSF中也一样,我在这里用两篇文章介绍两种方法来展示一下,如何在JSF中实现分页。本文假定你已经对JSF有了一些简单的了解,懂得基本配置和使用,并建立起一个blank项目。Myfaces是Apache基金会中的一个一级项目,除了实现JSF标准外,做了很多的扩展工作,在Myfaces包中有一个扩展包Tomahawk,我们将主要使用其中的两个Component实现分页:一个是,另一个是,在第一篇里面,我们简易的组合这两个Component来实现一种简单,但并不高效的分页。下面的例子来自于Myfaces-Sample,我省去了其中和分页逻辑无关的内容,详细的例子可以下载Myfaces-Sample包或者访问http:/www.irian.at/myfaces/home.jsf 查看。第一部分:dataTable在这一部分中,dataTable绑定了一个backing bean - pagedSort中的cars属性,我们可以在这个属性中加入数据访问逻辑,从数据库或者其他来源取得用于显示的数据。比如我们可以通过Hibernate获取一个List,其中包含有我们用于显示的POJOs。注意,dataTable中的rows属性指的是每页的行数,是必须指定的,否则是无法进行分页的,如果在项目中会使用固定行数的分页,建议把这个值写在BaseBackingBean中,并暴露一个property,供页面调用,所以每次在页面中就可以这么写#backingBean.pageSize。第二部分:dataScroller这里定义了我们用于分页的,最主要的是配置该分页Component针对哪个dataTable进行分页的“for”属性,该属性与dataTable绑定,并对其进行分页,在这里,绑定了第一部分中的id=data的dataTable,下面有很多的是指定分页的导航样式的,这里使用了图片作为导航,可以把他们改成文字形式的导航。当然这只是最简单,也是一种不推荐的分页方式,因为在每次进行分页的时候,将会从数据库中取回所有的记录放入List中,然后,dataScroller在对这个List进行分页,如果在数据量很大的情况下,这种方式显然是不符合要求的,假设每条记录占用1k内存,数据库中有100万条记录,每次要把这个List全部读取出来将占用1G内存。我们需要一种Load on demand方式的读取,也就是只在需要查看某页的时候读取该页的数据。另外一方面,JSF的生命周期中有多个阶段会调用到#pagedSort.cars中对应的方法,如果在这里调用了数据访问逻辑,就会在只显示一次页面的情况下进行多次数据库操作,也是相当的耗费资源的。所以我们需要有更好的分页方式去解决以上问题,下一篇我将介绍另一种方法以改善这些问题。前面一篇直接使用了Myfaces中的两个Component完成了一个简单的分页,这里将会介绍一种On-demand loading的方法来进行分页,仅仅在需要数据的时候加载。 先来说一些题外话,为了实现这种方式的分页,公司里大约5-6个人做了半个多月的工作,扩展了dataTable,修改了dataScrollor,以及各种其他的方法,但是都不是很优雅。在上个月底的时候,在Myfaces的Mail List中也针对这个问题展开了一系列的讨论,最后有人总结了讨论中提出的比较好的方法,提出了以下的分页方法,也是目前实现的最为优雅的方法,也就是不对dataTable和dataScrollor做任何修改,仅仅通过扩展DataModel来实现分页。 DataModel 是一个抽象类,用于封装各种类型的数据源和数据对象的访问,JSF中dataTable中绑定的数据实际上被包装成了一个DataModel,以消除各种不同数据源和数据类型的复杂性,在前面一篇中我们访问数据库并拿到了一个List,交给dataTable,这时候,JSF会将这个List包装成 ListDataModel ,dataTable访问数据都是通过这个DataModel进行的,而不是直接使用List。 接下来我们要将需要的页的数据封装到一个DataPage中去,这个类表示了我们需要的一页的数据,里面包含有三个元素:datasetSize,startRow,和一个用于表示具体数据的List。datasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取count即可,startRow表示该页的起始行在数据库中所有记录集中的位置。/*/ /* *Asimpleclassthatrepresentsapageofdataoutofalongerset,iea*listofobjectstogetherwithinfotoindicatethestartingrowandthefull*sizeofthedataset.EJBscanreturninstancesofthistypewhenreturning*subsetsofavailabledata. */ public class DataPage private int datasetSize; private int startRow; private Listdata; /*/ /* *Createanobjectrepresentingasublistofadataset.* param datasetSize*isthetotalnumberofmatchingrowsavailable.* param startRow*istheindexwithinthecompletedatasetofthefirstelement*inthedatalist.* param data*isalistofconsecutiveobjectsfromthedataset. */ public DataPage( int datasetSize, int startRow,Listdata) this .datasetSize = datasetSize; this .startRow = startRow; this .data = data; /*/ /* *Returnthenumberofitemsinthefulldataset. */ public int getDatasetSize() return datasetSize; /*/ /* *Returntheoffsetwithinthefulldatasetofthefirstelementinthe*listheldbythisobject. */ public int getStartRow() return startRow; /*/ /* *Returnthelistofobjectsheldbythisobject,whichisacontinuous*subsetofthefulldataset. */ public ListgetData() return data; 接下来,我们要对DataModel进行封装,达到我们分页的要求。该DataModel仅仅持有了一页的数据DataPage,并在适当的时候加载数据,读取我们需要页的数据。 /*/ /* *AspecialtypeofJSFDataModeltoallowadatatableanddatascrollertopage*throughalargesetofdatawithouthavingtoholdtheentiresetofdatain*memoryatonce.*Anytimeamanagedbeanwantstoavoidholdinganentiredataset,themanaged*beanshoulddeclareaninnerclasswhichextendsthisclassandimplements*thefetchDatamethod.Thismethodiscalledasneededwhenthetablerequires*datathatisntavailableinthecurrentdatapageheldbythisobject.*Thisdoesrequirethemanagedbean(andingeneralthebusinessmethodthat*themanagedbeanuses)toprovidethedatawrappedinaDataPageobjectthat*providesinfoonthefullsizeofthedataset. */ public abstract class PagedListDataModel extends DataModel int pageSize; int rowIndex;DataPagepage; /*/ /* *Createadatamodelthatpagesthroughthedatashowingthespecified*numberofrowsoneachpage. */ public PagedListDataModel( int pageSize) super (); this .pageSize = pageSize; this .rowIndex = - 1 ; this .page = null ; /*/ /* *Notusedinthisclass;dataisfetchedviaacallbacktothefetchData*methodratherthanbyexplicitlyassigningalist. */ public void setWrappedData(Objecto) if (o instanceof DataPage) this .page = (DataPage)o; else throw new UnsupportedOperationException( setWrappedData ); public int getRowIndex() return rowIndex; /*/ /* *Specifywhatthecurrentrowwithinthedatasetis.Notethatthe*UIDatacomponentwillrepeatedlycallthismethodfollowedbygetRowData*toobtaintheobjectstorenderinthetable. */ public void setRowIndex( int index) rowIndex = index; /*/ /* *Returnthetotalnumberofrowsofdataavailable(notjustthenumberof*rowsinthecurrentpage!). */ public int getRowCount() return getPage().getDatasetSize(); /*/ /* *ReturnaDataPageobject;ifoneisnotcurrentlyavailablethenfetch*one.Notethatthisdoesntensurethatthedatapagereturnedincludes*thecurrentrowIndexrow;seegetRowData. */ private DataPagegetPage() if (page != null ) return page; int rowIndex = getRowIndex(); int startRow = rowIndex; if (rowIndex = - 1 ) / evenwhennorowisselected,westillneedapage / objectsothatweknowtheamountofdataavailable. startRow = 0 ; / invokemethodonenclosingclass page = fetchPage(startRow,pageSize); return page; /*/ /* *ReturntheobjectcorrespondingtothecurrentrowIndex.IftheDataPage*objectcurrentlycacheddoesntincludethatindexthenfetchPageis*calledtoretrievetheappropriatepage. */ public ObjectgetRowData() if (rowIndex = datasetSize) throw new IllegalArgumentException( InvalidrowIndex ); if (rowIndex = endRow) page = fetchPage(rowIndex,pageSize);startRow = page.getStartRow(); return page.getData().get(rowIndex - startRow); public ObjectgetWrappedData() return page.getData(); /*/ /* *ReturntrueiftherowIndexvalueiscurrentlysettoavaluethat*matchessomeelementinthedataset.Notethatitmaymatcharowthatis*notinthecurrentlycachedDataPage;ifsothenwhengetRowDatais*calledtherequiredDataPagewillbefetchedbycallingfetchData. */ public boolean isRowAvailable() DataPagepage = getPage(); if (page = null ) return false ; int rowIndex = getRowIndex(); if (rowIndex = page.getDatasetSize() return false ; else return true ; /*/ /* *Methodwhichmustbeimplementedincooperationwiththemanagedbean*classtofetchdataondemand. */ public abstract DataPagefetchPage( int startRow, int pageSize); 最后,我们需要在Backing Bean中加一些东西,调用业务逻辑,并将数据交给PagedListDataModel,来帮我们完成最后的分页工作。 public SomeManagedBean . private DataPagegetDataPage( int startRow, int pageSize) / accessdatabasehere,orcallEJBtodoso public DataModelgetDataModel() if (dataModel = null ) dataModel = new LocalDataModel(20); return dataModel; private class LocalDataModel extends PagedListDataModel public LocalDataModel( int pageSize) super (pageSize); public DataPagefetchPage( int startRow, int pageSize) / callenclosingmanagedbeanmethodtofetchthedata return getDataPage(startRow,pageSize); 这里面有一个getDataPage的方法,只需要把所有业务逻辑的调用放在这里就可以了,最后业务逻辑调用的结果返回一个List,总条数返回一个int型的count放到DataPage中去就可以了。 为了实现复用,把上面第三段的代码中的LocalDataModel类和getDataPage方法抽到BasePagedBackingBean中,把getDataPage方法改成: protected abstract DataPage getDataPage(int startRow, int pageSize); 这样我们把所有需要分页的Backing Bean继承自这个抽象类,并实现getDataPage方法即可很容易的实现分页。 在具体应用中可以这么写: protected DataPagegetDataPage( int startRow, int pageSize) ListscheduleList = scheduleService.getSchedulesByDate(scheduleDate,startRow,pageSize); int dataSetSize = scheduleService.getSchedulesCountByDate(scheduleDate); return new DataPage(dataSetSize,startRow,scheduleList); 在数据访问中,我们只需要取出我们需要行数的记录就可以了,这在hibernate中非常容易实现。 如果使用Criteria查询的话,只要加上: criteria.setFirstResult(startRow); criteria.setMaxResults(pageSize); 使用Query查询的话,只要加上 query.setFirstResult(startRow); query.setMaxResults(pageSize); 并把两个参数传入即可。 我们还需要另外写一个Count的DAO,取出相同查询条件的记录条数即可。 还要修改一下Backing Bean中与dataTable绑定的property,将返回类型由List改成DataModel,而第一篇中用到的页面不需要做任何修改就可以满足新的需求了。 里面最重要的是 PagedListDataModel 中 fetchPage 这个方法,当满足取数据的条件时,都会调用它取数据,因为业务逻辑不同,不便于将业务逻辑的调用放在里面实现,于是将其作为抽象方法,将具体的实现放到具体的Backing Bean中进行,在BaseBackingBean中,实现了这个方法,调用了getDataPage(startRow, pageSize)这个方法,而在BaseBackingBean中,这个方法又推迟到更具体的页面中实现,这样,我们在具体的页面中只需要实现一个getDataPage(startRow, pageSize)这个方法访问业务逻辑。 大功告成,这个实现把前面遇到的两个问题都解决了, On-demand loading 是没有问题了,因为只有在首次读取和换页的时候DataModel才会向数据库请求数据,虽然在JSF的生命周期中多次调用与dataTable绑定的方法,但是因为每次业务逻辑请求以后,数据都会存放在DataPage中,如果里面的数据满足需求的话,就不再请求访问数据库,这样多次访问数据库的问题也解决了。 虽然这样的话,dataScrollor的Tag使用起来还是很复杂,通常在同一个项目中,我们只会使用一种样式的分页导航,不过没关系,我们只需要修改以下DataScrollor的Render Kit,把一些可以定义的值固定下来,再定义一个TLD文件,就可以在项目中使用简化版的Tag了。 这个方法一开始发布在Myfaces的Wiki中,/myfaces/WorkingWithLargeTables,那里很少有人关注到,大家有兴趣可以看看原文,本文只是对这种方法做一些简单的介绍,并非自创,希望大家能够多多关注开源社区,因为那里有最新最好的东西。 从Nightly Build服务器中拿到的12.27的Myfaces包,发现里面扩充了很多新的Component,只是并没有正式发布,大家有兴趣的话可以研究研究。 好久没有写点东西了,这次想把JSF中的分页系列文章再扩充一点,说明一下查询和分页结合的情况,当我们把查询条件和查询结果放到一个页面上时,查询还是非常容易实现的,甚至不需要我们手工去从数据库中查询。 在本系列文章中的第二篇中,介绍了一种 Load On Demand的方式,我们在这里需要继续利用这种方式,并对其做一些小小的扩展。这里我们使用 Hibernate3 作为持久化方案。 简单的介绍一下应用情景,一个系统中包含了一些 Customer 的信息,我们需要对其进行查询并对查询结果进行分页。 首先处理条件查询的情况,通常会根据 VO 中的字段进行 like 型查询,有时候时间或数字之类的会使用Between查询,因为查询条件一般不会很复杂,在这里,使用 Hibernate3 中的 Criteria 查询来处理这样的情况,我们把所有的查询条件通过 Customer 这个 VO 传进来,然后只对非空字段进行 like 查询,我们用到这样的方法。 public ListqueryByConditions(Customercustomer, int startRow, int pageSize)Criteriacriteria = getSession().createCriteria(Customer. class ); if ( ! StringUtils.isEmpty(customer.getCustomerName()criteria.add(QueryUtils.getCriteriaParam( customerName ,customer.getCustomerName(); if ( ! StringUtils.isEmpty(customer.getAddress()criteria.add(QueryUtils.getCriteriaParam( address ,customer.getAddress(); if ( ! StringUtils.isEmpty(customer.getFax()criteria.add(QueryUtils.getCriteriaParam( fax ,customer.getFax(); return criteriaPagedList(criteria,startRow,pageSize); 另外对应的一个count方法略去,只需要在前面加入一个criteria.setProjection(Projections.count(customerId); 因为考虑到以后的扩展,使用了一个Utils方法,QueryUtils.getCriteriaParam方法 public static final SimpleExpressiongetCriteriaParam(Stringname,Stringparam) return Expression.lik
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年四川职业技术学院单招职业适应性测试题库及答案解析(夺冠系列)
- 2026年哈尔滨铁道职业技术学院单招职业技能测试必刷测试卷附答案解析
- 2026年泉州职业技术大学单招综合素质考试题库附答案解析
- 2026年河南水利与环境职业学院单招职业适应性考试必刷测试卷带答案解析
- 2026年云南省德宏傣族景颇族自治州单招职业适应性测试题库及答案解析(名师系列)
- 塑料型材产业链研究
- 房屋托管协议合同书
- 房屋拆除买卖协议书
- 房屋搬迁补助协议书
- 房屋水管购买协议书
- 初中数学统计与概率专题训练50题含答案
- 国防知识竞赛题库-国防知识竞赛试题及答案
- 血管活性药物静脉输注护理的团标解读课件
- 2025年安全员之B证(项目负责人)通关考试试题及答案
- 激发小学生对科学实验的兴趣的教学方法
- 老年人皮内注射注意事项
- 大田县鹭坪科技有限公司年产25万吨富锰渣新建工程环境影响报告书
- 医院培训课件:《医疗器械相关压力性损伤预防新进展》
- 3地质勘查项目预算标准
- 品管圈应用于-神经外科提高24小时出入量-准确率
- 过程控制课程设计-前馈-反馈控制系统仿真论文
评论
0/150
提交评论