Pageable DisplayTag with Hibernate
啊~~~~~~ 昨、今兩天都跟 DisplayTag 大戰。搞了好久總算搞定了。DisplayTag 這個 taglib 寫的真很不錯,介面很好看,而且 pageable, sort, group 使用上都很簡單。最後還有無限變化的 decorator,設計的人真的非常有經驗!然而它還是有一些地方還不夠完美。使用它的 page 功能,比方說120筆分12頁,每頁10筆好了,它沒有機制可以讓你一次只讀10筆。一定要一次讀完 120 筆才行。這樣分頁的用意就少了一大半了!這兩天就都花在這玩意上,浪費了好多時間。最後的答案還算差強人意。
HibernatePageList page = new HibernatePageList(pageSize, "from Foo f where f.id >20" ) ;
page.loadPage(2) ;
HibernatePageList 是一個帶有 iterator() method 的 list (不全是,沒有 implement List) 。這個物件裡面會有空的 list,裡面全部裝滿 null,整個 list 的大小就是 上面 "from... >20" query 出來的筆數。當執行 loadPage(2) 時,內部會去抽第二頁所需的資料,然後 HibernatePageList這個物件的內部的List 的第二頁會被填滿(其餘保持 null) 。如此當使用者點到分頁的第二頁時,在 servlet/action 裡執行 loadPage(2),就可以讀取需要顯示的值。
displayTag 本身並沒有提供讀取目前是第幾個分頁的功能。我們只好從它的 request parameter 下手;當 displayTag 分頁時,比如說第三頁,它會在 url 後面接上 "&d-12345-p=3" 這樣的 parameter 。12345 是它產生的隨機碼,有時是四個數字。抓取這個數字可用 regular expression : params.match("d-[0-9]+-p") 這個 pattern 來找,找到後便可傳給 loadPage. 所以最後可簡化為:
page.loadPage(request) ; // get current pageNo from request
完整的程式碼如下:
public class HibernatePageList {
	private List cachedList;
	private int pageNo;
	private int pageSize;
	private int totalSize;
	private String queryString;
	public HibernatePageList(int pageSize, int totalSize, String queryString) {
		this.queryString = queryString;
		this.pageSize = pageSize;
		this.totalSize = totalSize;
		// initialize full list with null;
		cachedList = new ArrayList(totalSize);
		for (int i = 0; i < totalSize; i++) {
			cachedList.add(null);
		}
	}
	public HibernatePageList(int pageSize, String fromBasedQueryString)
		throws HibernateException {
		this(
			pageSize,
			getTotalSize(fromBasedQueryString),
			fromBasedQueryString);
	}
	private static int getTotalSize(String fromBasedQueryString)
		throws HibernateException {
		return (
			(Integer) HibernateUtil
				.currentSession()
				.iterate("select count(*) " + fromBasedQueryString)
				.next())
			.intValue();
	}
	public void loadPage(final int pageNo) throws HibernateException {
		//check cache has data:
		if (pageIsCached(pageNo)) {
			return;
		}
		Query query = HibernateUtil.currentSession().createQuery(queryString);
		query.setMaxResults(pageSize);
		query.setFirstResult(getPageStartRow(pageNo));
		final Iterator iter = query.iterate();
		for (int i = getPageStartRow(pageNo); iter.hasNext(); i++) {
			cachedList.set(i, iter.next());
		}
	}
	public void loadPage(HttpServletRequest request)
		throws HibernateException {
		loadPage(getDisplayTagPageNo(request));
	}
	/**
	 * extract page no from request of display tag
	 * 
	 * @param request
	 * @return
	 * @throws NumberFormatException
	 */
	public int getDisplayTagPageNo(HttpServletRequest request)
		throws NumberFormatException {
		Enumeration names = request.getParameterNames();
		int pageNo = 1;
		while (names.hasMoreElements()) {
			String parameter = (String) names.nextElement();
			if (parameter.matches("d-[0-9]+-p")) {
				pageNo = Integer.parseInt(request.getParameter(parameter));
				break;
			}
		}
		return pageNo;
	}
	private int getPageEndRow(int pageNo) {
		return Math.min(pageNo * pageSize, totalSize);
	}
	private int getPageStartRow(int pageNo) {
		return (pageNo - 1) * pageSize;
	}
	private boolean pageIsCached(final int pageNo) {
		boolean pageIsCached = true;
		for (int i = getPageStartRow(pageNo);
			i < getPageEndRow(pageNo) && pageIsCached;
			i++) {
			if (cachedList.get(i) == null) {
				pageIsCached = false;
			}
		}
		return pageIsCached;
	}
	public Object get(int index) {
		return cachedList.get(index);
	}
	public boolean isEmpty() {
		return cachedList.isEmpty();
	}
	public Iterator iterator() {
		return cachedList.iterator();
	}
	public int size() {
		return cachedList.size();
	}
	public int getPageSize() {
		return pageSize;
	}
	public String getQueryString() {
		return queryString;
	}
}
    
        
<< Home