Java前端框架ZK的分页组件

 本文只针对ZK的分页数据表格这个主题,关于ZK框架的入门介绍见《ZK(The leading enterprise Ajax framework)入门指南》

 https://blog.csdn.net/daquan198163/article/details/9304897?spm=1001.2014.3001.5501 

概述

zk框架虽然提供了大量方便的开箱即用的组件,但是不得不说分页是个例外,无论是listbox还是grid,对于大数据量的场景都没有给出开箱即用的分页解决方案。

<listbox mold="paging">这种内存分页是无法满足真实应用的——并发量和数据量一高就把内存耗尽了。因此需要我们自行封装解决。

分页逻辑的模板类BaseSortPagingListModel

首先需要设计一个模板类把分页的通用逻辑、流程(构造criteria查询条件、获取总记录数、查询当前页数据、处理返回结果)封装起来,以便复用和扩展。

同时这个模板还需要能够与最终用来显示数据表格的zk组件集成。

因此最合理的方案就是基于AbstractListModel扩展出一个分页版本——BaseSortPagingListModel

package common;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.zul.AbstractListModel;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.ListModelExt;
import org.zkoss.zul.event.ListDataEvent;

/**
 * ZK分页ListModel基类,用于封装分页逻辑:构造criteria、获取总记录数、查询当前页数据、处理返回结果
 * @param <TC> 查询条件model泛型
 * @param <TM> 返回结果集中的model泛型
 */
@SuppressWarnings({ "unchecked", "serial", "rawtypes", "deprecation" })
abstract public class BaseSortPagingListModel<TC, TM> extends AbstractListModel<TM> implements ListModelExt {
	protected static final Logger logger = LoggerFactory.getLogger(BaseSortPagingListModel.class);

	private static final ThreadLocal<Map<String, Integer>> threadLocalTotalSize = new ThreadLocal<Map<String, Integer>>();
	private static final ThreadLocal<Map<String, Map>> threadLocalCurrPageCache = new ThreadLocal<Map<String, Map>>();

	private int pageSize;

	private String orderBy;
	private Boolean orderByAsc;
	private BaseSortPagingListModelPostQueryProcessor postQueryProcessor;

	public void setPostQueryProcessor(BaseSortPagingListModelPostQueryProcessor postQueryProcessor) {
		this.postQueryProcessor = postQueryProcessor;
	}

	public BaseSortPagingListModel(int pageSize, String orderBy, Boolean orderByAsc) {
		super();
		this.pageSize = pageSize;
		this.orderBy = orderBy;
		this.orderByAsc = orderByAsc;
		super.setPageSize(pageSize);
	}

	/**
	 * 构造查询条件
	 */
	abstract protected TC constructCriteria(String orderBy, Boolean orderByAsc);

	/**
	 * 返回第index条数据:
	 * 首先从当前线程缓存查找,找到直接返回,
	 * 如果缓存中不存在,就调用具体子类doQuery查询当前页数据,并放入缓存
	 */
	@Override
	public final TM getElementAt(int index) {
		Map<Integer, TM> cache = this.getCurrentPageDataCache();

		TM li = cache.get(index);
		if (li == null) {
			long start = System.currentTimeMillis(); //获取开始时间
			cache.clear();
			TC criteria = constructCriteria(orderBy, orderByAsc);
			List result;
			try {
				result = doQuery(criteria, index, pageSize);
				this.doPostQuery(index);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				result = new ArrayList();
			}

			int i = index;
			for (Object obj : result) {
				li = doTransferResultModel(obj);
				cache.put(i, li);
				i++;
			}
			long end = System.currentTimeMillis(); //获取结束时间
			if (pageSize >= 100) {
				logger.debug("TM getElementAt(int index) 当前页 查询耗时:" + (end - start) + " ms");
			}
		} else {
			return li;
		}
		li = cache.get(index);
		if (li == null) {
			return handlePagingException(index);
		} else {
			return li;
		}
	}

	/**
	 * 查询一页数据后处理
	 * @param index
	 */
	private void doPostQuery(int index) {
		if (postQueryProcessor != null) {
			postQueryProcessor.doPostQuery(index);
		}
	}

	/**
	 * 
	 * @param index
	 */
	protected TM handlePagingException(int index) {
		String error = "index[" + index + "]数据找不到,total[" + this.getSize() + "]" + this;
		logger.error(error);
		throw new RuntimeException(error);
	}

	/**
	 * 查询当前页数据
	 */
	abstract protected List doQuery(TC criteria, int index, int pageSize) throws Exception;

	/**
	 * 加工处理查询返回值,转换成前端展现需要的model格式
	 */
	abstract protected TM doTransferResultModel(Object obj);

	/**
	 * 获取当前页数据缓存
	 */
	protected Map<Integer, TM> getCurrentPageDataCache() {
		Map<String, Map> map = threadLocalCurrPageCache.get();
		if (map == null) {
			map = new HashMap<String, Map>();
			threadLocalCurrPageCache.set(map);
		}
		Map<Integer, TM> cache = map.get(getChildClassCacheKey());
		if (cache == null) {
			cache = new HashMap<Integer, TM>();
			map.put(getChildClassCacheKey(), cache);
		}
		return cache;
	}

	protected Integer getSizeCache() {
		Map<String, Integer> map = threadLocalTotalSize.get();
		if (map == null) {
			map = new HashMap<String, Integer>();
			threadLocalTotalSize.set(map);
		}
		return map.get(getChildClassCacheKey());
	}

	protected void setSizeCache(Integer size) {
		Map<String, Integer> map = threadLocalTotalSize.get();
		if (map == null) {
			map = new HashMap<String, Integer>();
			threadLocalTotalSize.set(map);
		}
		map.put(getChildClassCacheKey(), size);
	}

	/**
	 * memo:为避免1个页面中包含多个分页列表时数据错乱,需要在CACHE_KEY_DATA和CACHE_KEY_SIZE添加分页model对象的hashcode信息
	 */
	protected String getChildClassCacheKey() {
		return hashCode() + "";
	}

	/**
	 * 供ZK框架调用的方法:获取总记录数
	 * 由于ZK框架会在处理第一次查询请求渲染页面过程中非常多次调用此方法,因此必须缓存以免影响性能!
	 */
	@Override
	public final int getSize() {
		Integer size = this.getSizeCache();// (Integer) Executions.getCurrent().getAttribute(CACHE_KEY_SIZE + getChildClassCacheKey());
		if (size == null) {
			TC criteria = constructCriteria(null, null);//查询总记录数时不应该orderby
			try {
				size = doCountTotalSize(criteria);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				return 0;
			}
			this.setSizeCache(size);
			return size;
		} else {
			return size;
		}
	}

	@Override
	public void sort(Comparator comparator, boolean ascending) {
		if (comparator instanceof FieldComparator) {
			this.orderBy = ((FieldComparator) comparator).getRawOrderBy();
			this.orderByAsc = ascending;
			logger.debug("order by " + orderBy + " " + (ascending ? "asc" : "desc"));
			getCurrentPageDataCache().clear();//如果不清理掉threadlocal cache,不会重新按照新的sort查询
			fireEvent(ListDataEvent.CONTENTS_CHANGED, -1, -1);
		}
	}

	@Override
	public String getSortDirection(Comparator comparator) {
		String rawOrderBy = ((FieldComparator) comparator).getRawOrderBy();
		logger.debug(rawOrderBy);
		return rawOrderBy;
	}

	/**
	 * 获取总记录数
	 */
	abstract protected Integer doCountTotalSize(TC criteria) throws Exception;

	/**
	 * 清理分页状态,避免干扰下次查询
	 */
	public static void clearPagingState() {
		threadLocalTotalSize.set(null);
		threadLocalCurrPageCache.set(null);
	}

}

扩展分页模板 实现分页查询的服务端逻辑

BaseSortPagingListModel模板类通过第一个泛型参数TC对查询逻辑以及回调方法abstract protected TC constructCriteria抽象了底层的实现细节,因此不会限制你用什么持久层用什么查询API,

如下代码样例不难看出是在用Hibernate的criteria API实现分页查询:

private static final int DEFAULT_PAGE_SIZE = 20;

private void doPagingQuery() {
		BaseSortPagingListModel<DetachedCriteria, FiDcForecastBean> listModel= preparePagingListModel(DEFAULT_PAGE_SIZE);
		
		renderListboxPage(forecastListbox, 0, listModel);
	}

@SuppressWarnings("serial")
	private BaseSortPagingListModel<DetachedCriteria, FiDcForecastBean> preparePagingListModel(int pageSize) {
		return new BaseSortPagingListModel<DetachedCriteria, FiDcForecastBean>(pageSize , sortColum,
				sortAscFlag) {

			@Override
			protected DetachedCriteria constructCriteria(String orderBy, Boolean asc) {
				return prepareDc(forecastTaskTypeList, queryForm);
			}

			@Override
			protected Integer doCountTotalSize(DetachedCriteria dc) throws Exception {
				return HibernateUtil.countRecordsByCriteria(dc, "id");
			}

			@Override
			protected List doQuery(DetachedCriteria dc, int index, int pageSize) throws Exception {
				return HibernateUtil.findByCriteria(dc, index, pageSize);
			}

			@Override
			protected FiDcForecastBean doTransferResultModel(Object item) {
				return (FiDcForecastBean) item;
			}
		};
	}

public static void renderListboxPage(Listbox lb, int currActivePage, BaseSortPagingListModel pagingListModel) {
		lb.getPaginal().setDetailed(true);
		lb.getPaginal().setPageSize(pagingListModel.getPageSize());
		int size = pagingListModel.getSize();
		lb.getPaginal().setTotalSize(size);
		lb.setModel(pagingListModel);
		lb.getPaginal().setActivePage(currActivePage);
		lb.invalidate();
	}

页面

<listbox id="forecastListbox" hflex="true" emptyMessage="no data" mold="paging"
						nonselectableTags="*" multiple="false" checkmark="false">
    <listhead。。。。。。
    <template name="model">
		<listitem>
			<custom-attributes data="${each}" />									
			<listcell label="${forEachStatus.index+1}" />
			<listcell label="${each.po}" />
			<listcell label="${each.colour}" />
        </listitem>
	</template>
</listbox>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值