/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jef.database; import java.sql.SQLException; import java.util.Collections; import java.util.Iterator; import java.util.List; import jef.common.wrapper.Page; import jef.database.wrapper.populator.Transformer; import jef.tools.PageInfo; /** * 用于分页查询的遍历器,用于使用分页来查询结果<br> * 实现了Iterator<List<T>>接口,即每次调用next()方法可以得到下一页的数据<br> * * <ul> * <li> {@link #getTotal()}<br> * 得到总记录条数(JEF会自动将你的查询语句改写为等效的count语句进行查询)</li> * <li>getTotalPage()<br> * 得到总页数</li> * <li>hasNext()可以判断是否可以继续向下翻页。</li> * <li>setOffset(int) 可以设置当前记录读取从第几个开始,例如0表示从第一条记录开始,1表示从第二条开始.<br> * <li>getCurrentPage()可以获得当前的页号</li> * 每页的size可以在构造的时候传入。<br> * </ul> * * @author Administrator * * @param <T> */ public abstract class PagingIterator<T> implements Iterator<List<T>> { /** * 分页信息 */ protected PageInfo page; /** * 记录转换结果类型 */ protected Transformer transformer; /** * 最后一页 * FIXME 这个设计有点冗余 */ protected int lastPage = -1; /* * TODO 用于报表或导出时,使用Oracle共享锁,阻止记录被插入和更改。 * 关于数据导出时分页的一致性问题。 一个方法是加上共享锁,这样所有的写操作都会挂起。 * 还有一个方式是使用create table as * 获得一个记录快照,然后根据快照来分页获取数据,保证 整个处理过程中,不会因为有数据被插入或者顺序变化,造成记录的错漏和重复。 */ /** * 获取总记录数 * * @return 总记录数, int类型 */ public int getTotalAsInt() { long total = getTotal(); if (total > Integer.MAX_VALUE) { throw new IllegalStateException(); } return (int) total; } /** * 获取总记录数 * * @return */ public long getTotal() { calcPage(); return page.getTotal(); } /** * 获取总页数 * * @return 总页数。0表示没有记录,有一条记录时也是1页。 */ public int getTotalPage() { calcPage(); return page.getTotalPage(); } /** * 获得当前页数 * * @return */ public int getCurrentPage() { return page.getCurrentPage(); } /** * 获得每页记录条数 * * @return */ public int getRowsPerPage() { return page.getRowsPerPage(); } /** * 设置查询结果偏移 * * @param offset 从0开始。如要跳过前10条记录则输入10,查询结果将从第11条记录开始 * @return PagingIterator对象本身 */ public PagingIterator<T> setOffset(int offset) { page.setOffset(offset); return this; } /** * 设置下一次调用 {@link #next()}方法要读取的页数。 * * @param pageNum * 页数,从1开始。如果大于总页数会下调至最后一页。 * @return PagingIterator对象本身 */ public PagingIterator<T> setCurrentPage(int pageNum) { int totalPage = this.getTotalPage(); if (pageNum > totalPage) { pageNum = totalPage; } page.setCurPage(pageNum); return this; } /** * 得到在指定页面的所有数据 * * @param pageNum 页号,从1开始 * @return 查询结果 */ public List<T> getRecordsInPage(int pageNum) { calcPage(); int old=page.getCurrentPage(); page.setCurPage(pageNum); try { return doQuery(true); } catch (SQLException e) { throw DbUtils.toRuntimeException(e); }finally{ page.setCurPage(old); } } /** * 下一页 */ @SuppressWarnings("unchecked") public List<T> next() { calcPage(); try { if (page.getCurrentRecordRange().getLimit() <= 0) { return Collections.EMPTY_LIST; } return doQuery(true); } catch (SQLException e) { throw new RuntimeException(e); } finally { if (!page.gotoNext()) { lastPage = page.getCurrentPage() - 1; } } } /** * 子类实现,完成查询 * * @param b 是否执行分页逻辑 * @return * @throws SQLException */ protected abstract List<T> doQuery(boolean b) throws SQLException; /** * 子类实现,计算记录条数 * * @return 总记录条数 */ protected abstract long doCount() throws SQLException; public boolean hasNext() { calcPage(); if (lastPage > -1) { return page.getCurrentPage() < lastPage; } return page.hasNext(); } public void remove() { throw new UnsupportedOperationException(); } /** * 获得当前页的全部数据 * * @return */ public Page<T> getPageData() { return new Page<T>((int) getTotal(), next(),page.getRowsPerPage()); } // 获取总数,用来计算分页 protected void calcPage() { if (page.getTotal() == -1) { recalculatePages(); } } /** * 强制重新计算一次页数<br> * 默认情况下,页数只计算一次。 调用此方法可以即时再计算一次页数。 */ public void recalculatePages() { try { page.setTotal(doCount()); } catch (SQLException e) { throw DbUtils.toRuntimeException(e); } } // 如果在不获取总数的情况下任意(顺序)翻页,当翻到全空的一页时,就知道前一页就是最后一页,从而能够计算出总数了,防止继续翻页 protected void recordEmpty() { if (lastPage < 0 || page.getCurrentPage() < lastPage) { lastPage = page.getCurrentPage() - 1;// 记录 } } }