




已閱讀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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年安徽省中考英語試卷及答案
- 景區餐飲單位管理制度
- 公司圖書館安全管理制度
- 幼兒園退休檔案管理制度
- ktv員工保密管理制度
- 旅游案頭安全管理制度
- 旅店治安保衛管理制度
- 招商部制度流程管理制度
- 外貿公司訂單部管理制度
- 病毒高考試題及答案
- 大廈物業移交接收方案(標準版)
- 卅鋪初級中學食品安全存在問題整改方案
- 職業技術學院《數控編程與加工》課程標準
- DB14T-苜蓿草顆粒生產技術規程
- 2024至2030年中國番茄行業研究及市場投資決策報告
- 《會計英語實訓教程》(高職)全套教學課件
- 工信部:2024水泥行業節能診斷服務指南報告
- 海南省2021年初中生物學業水平考試仿真模擬卷
- 01J925-1壓型鋼板、夾芯板屋面及墻體建筑構造
- GB/T 44190-2024政務服務便民熱線集成規范
- 湖南省長沙市2024年七年級下冊生物期末試卷附答案
評論
0/150
提交評論