Sarkuya的编程园地

-- 炼志飞心砚香墨,冷暖春秋隐翠竹。

用户操作
[即时聊天] [发私信] [加为好友]
唐辉武ID:tanghw
21205次访问,排名5592(-2)好友0人,关注者0
tanghw的文章
原创 30 篇
翻译 2 篇
转载 0 篇
评论 8 篇
Sarkuya的公告

转载请注明作者及出处


最近评论
hitetoshi:其实在VC6开发环境中没这么复杂,可以直接在asm文件上右键菜单选择“setting”,在Custom Build上设置Outputs为$(IntDir)\$(InputName).obj,Command为C:\MASM32\Bin\ml.exe /c /coff /nologo /IC:\MASM32\include\w2k /IC:\MASM32\macros /IC:\MASM32\I……
java爱好:很好,找了很久了,LZ能发一个全一点的例子给我吗?ljnone@yahoo.com.cn,万分感谢~
Y:谢谢!!!写得太有用了!!谢谢!!
fangzhou:似乎你很久不来这个地方了,不知你还看不看的到。

想征集一些关于Netbeans方面的稿件,不知你是否有时间。经过审核,录用在CSDN网站上以及《程序员》上的文章都会给予稿酬。

欢迎你以任何方式联系我。

…………………………………………………
方舟 商务部

CSDN.NET--集成化信息传播与服务平台 &a……
crdchen:原来还有这样的事情,谢谢楼主解决公布哈.
文章分类
收藏
    相册
    Hot Ones
    GDI+ for VC6 MFC
    MASM32快速起步
    NetBeans专题
    使用JMock来实现孤立测试
    关于Java栈与堆的思考
    揭开AJAX神秘面纱
    改进Spring中的分页技术
    数据库设计5步骤
    活用SiteMesh支撑网站结构
    装饰Servlet Request对象
    跨进Java的门票
    Java
    Java之家
    Matrix
    NetBeans
    存档
    订阅我的博客
    XML聚合  FeedSky

    原创 改进Spring中的分页技术收藏

    新一篇: 活用SiteMesh,一个装饰器就可支撑整个网站结构 | 旧一篇: GDI+ for VC6 MFC

    Spring中有一个PagedListHolder,可以实现分页。但此类有几个缺点:

    1. 使用此类的代码比较繁琐
    2. 此类存放的数据源是所有的记录集,即对于记录数为1000条的数据,即使我们只需在一个页面中显示10条记录,每次均需要检索1000条记录出来,并且没有内在的缓存机制
    3. 如果需将pageSize, maxLinkedPages这些一般为Session级的变量存于Session中,则必须在Session中存放PagedListHolder,从而导致大容量的数据常常撑满了Session
    4. 只是实现了Serializable标识接口,且getPage(), setPage(), setPageSize()方法中直接使用newPageSet (private) 的属性,不利于子类覆盖。而且,内部类的各个方法耦合极强。特定方法的使用必须信赖于某个方法或标志变量作为前提条件。

    比较理想的情况是,根据每一个HttpServletRequest产生一个PagesListHolder,不管记录总数有多少个,每次只检索页面上所显示的记录,但将pageSize, maxLinkedPages设为Session级的效果。

    鉴于上述几点,我从Spring原有的PagedListHolder抽取出一些必需的方法名作为接口,并以一个名为RequestPagedListHolder的类实现之。

    下面是抽取出来的PagedListHolder接口。

    package com.sarkuya.web.pagination;

    import java.io.Serializable;
    import java.util.List;

    /**
     *
     * @author Sarkuya
     */
    public interface PagedListHolder extends Serializable {
        public static final int DEFAULT_PAGE_SIZE = 10;
        public static final int DEFAULT_MAX_LINKED_PAGES = 10;
       
        public void setRecordsSubst(List recordsSubset);
        public void setRealRecordCount(long realRecordCount);
       
        /**
         * 设置每页应有多少条记录。
         */
        public void setPageSize(int pageSize);
       
        /**
         * 返回每页共有多少条记录
         */
        public int getPageSize();
       
        /**
         * 根据pageSize,返回共有多少页
         */
        public int getPageCount();
       
        /**
         * 返回当前页码。
         * 首页为0
         */
        public int getPage();
       
        /**
         * 设置当前页码。
         * 首页为0
         */
        public void setPage(int page);
       
        /**
         * 设置围绕当前页最多可以显示多少链接的页数。
         * 此方法<strong>会</strong>影响getFirstLinkedPage()及getLastLinkedPage()
         */
        public void setMaxLinkedPages(int maxLinkedPages);
       
        /**
         * 返回围绕当前页最多可以显示多少链接的页数
         */
        public int getMaxLinkedPages();
       
        /**
         * 返回首页的页码
         */
        public int getFirstLinkedPage();
       
        /**
         * 返回最后一页的页码
         */
        public int getLastLinkedPage();
       
       
        /**
         * 转至前一页。
         * 如果已经是首页,则停在该页。
         */
        public void previousPage();
       
        /**
         * 转至下一页。
         * 如果已经是最后一页,则停在该页。
         */
        public void nextPage();
       
        /**
         * 转至首页。
         */
        public void firstPage();
       
        /**
         * 转至最后一页
         */
        public void lastPage();
       
        /**
         * 返回总的记录数
         */
        public long getNrOfElements();
       
        /**
         * 返回在当前页面上的第一个记录在所有记录(从0开始)中的编号
         */
        public int getFirstElementOnPage();
       
        /**
         * 返回在当前页面上的最后一个记录在所有记录(从0开始)中的编号
         */
        public int getLastElementOnPage();
       
        /**
         * 返回在当前页面上的所有记录
         */
        public List getPageList();
    }

    setRecordsSubst()用于存放页面显示的记录源,而setRealRecordCount()用于记录满足条件的记录总数。

    下面是此接口的实现:

    package com.sarkuya.web.pagination;

    import java.util.List;
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.web.bind.ServletRequestDataBinder;

    /**
     *
     * @author Sarkuya
     */
    public class RequestPagedListHolder implements PagedListHolder {
        private static int pageSize = DEFAULT_PAGE_SIZE;
        private static int maxLinkedPages = DEFAULT_MAX_LINKED_PAGES;
       
        private int page = 0;
        private List recordsSubset;
       
        private long realRecordCount;
       
        /** Creates a new instance of RequestPagedListHolder */
        public RequestPagedListHolder(HttpServletRequest request, long realRecordCount, PagedListProvider pagedListProvider) {
            setRealRecordCount(realRecordCount);
           
            ServletRequestDataBinder binder = new ServletRequestDataBinder(this);
            binder.bind(request);
           
            checkPageNavigation(request);
           
            setRecordsSubst(pagedListProvider.getRecordsSubset(getPageSize() * getPage(), getPageSize()));
        }

        private void checkPageNavigation(final HttpServletRequest request) {
            String pageNavAction = request.getParameter("pageNavAction");
            if (pageNavAction != null) {
                if (pageNavAction.equals("firstPage")) {
                    firstPage();
                } else if (pageNavAction.equals("previousPage")) {
                    previousPage();
                } else if (pageNavAction.equals("nextPage")) {
                    nextPage();
                } else if (pageNavAction.equals("lastPage")) {
                    lastPage();
                }
            }
        }
       
        public void setRecordsSubst(List recordsSubset) {
            this.recordsSubset = recordsSubset;
        }
       
        public void setRealRecordCount(long realRecordCount) {
            this.realRecordCount = realRecordCount;
        }
       
        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }
       
        public int getPageSize() {
            return pageSize;
        }
       
        public int getPageCount() {
            float nrOfPages = (float) getNrOfElements() / getPageSize();
            return (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages);
        }
       
        public int getPage() {
            if (page >= getPageCount()) {
                page = getPageCount() - 1;
            }
            return page;
        }
       
        public void setPage(int page) {
            this.page = page;
        }
       
        public void setMaxLinkedPages(int maxLinkedPages) {
            this.maxLinkedPages = maxLinkedPages;
        }
       
        public int getMaxLinkedPages() {
            return maxLinkedPages;
        }
       
        public int getFirstLinkedPage() {
            return Math.max(0, getPage() - (getMaxLinkedPages() / 2));
        }
       
        public int getLastLinkedPage() {
            return Math.min(getFirstLinkedPage() + getMaxLinkedPages() - 1, getPageCount() - 1);
        }
       
        public void previousPage() {
            if (!isAtFirstPage()) {
                page--;
            }
        }
       
        public void nextPage() {
            if (!isAtLastPage()) {
                page++;
            }
        }
       
        public void firstPage() {
            setPage(0);
        }
       
        public void lastPage() {
            setPage(getPageCount() - 1);
        }
       
        public long getNrOfElements() {
            return realRecordCount;
        }
       
        public int getFirstElementOnPage() {
            return (getPageSize() * getPage());
        }
       
        public int getLastElementOnPage() {
            int endIndex = getPageSize() * (getPage() + 1);
            return (endIndex > getNrOfElements() ? (int)getNrOfElements() : endIndex) - 1;
        }
       
        public List getPageList() {
            return recordsSubset;
        }
       
        public boolean isAtFirstPage() {
            return getPage() == 0;
        }
       
        public boolean isAtLastPage() {
            return getPage() == getPageCount() - 1;
        }
    }

    此类有以下特点:

    1. pageSize及maxLinkedPages均设为static,这样不因每个Request而改变。因此用户不必每次显示一个不同的页面后都在UI中重新设置它们。
    2. 在构造函数中包装了所有的使用过程,既简化了该类的使用,也保证了该类被正确初始化。
    3. 摒弃了newPageSet变量,减少了各个方法的耦合强度。
    4. 在Spring环境中使用了ServletRequestDataBinder,大大简化了各个参数的读取设置过程。
    5. 通过回调机制,每次只检索PagedListProvider所提供的记录子集,节约了内存,提高了程序效率。

    不难看出,PagedListProvider是个接口,只有一个方法:

    package com.sarkuya.web.pagination;

    import java.util.List;

    /**
     *
     * @author Sarkuya
     */
    public interface PagedListProvider {
        public List getRecordsSubset(int firstResult, int maxResults);
    }

    熟悉Hibernate的用户知道,Hibernate中就是需要这两个参数来实现分页了。如果不使用Hibernate,也没关系,自己实现此接口就行了。(接口实现起来很简单,但技术细节却非简单,Hibernate用户在此居于明显的优势)

    以上的两个接口,一个实现类,便是经过改进后的分页技术了。下面看其使用方法。

    当用户需要查看带有分面功能的页面时,都会由下面的方法处理:

        private void setPageListForView(HttpServletRequest request, ModelAndView mav, final String tableName) {
            long totalRecordsCount = adminService.getTotalRecordCount(tableName);

            PagedListProvider listProvider = new PagedListProvider() {
                public List getRecordsSubset(int firstResult, int maxResults) {
                    return (List) adminService.listTable(tableName, firstResult, maxResults);
                }
            };
           
            PagedListHolder holder = new RequestPagedListHolder(request, totalRecordsCount, listProvider);
           
            mav.addObject("pagedRecords", holder);
        }

    这是一个简单的方法,为RequestPagedListHolder的构造函数准备好实参后,生成一个实例,将其置于页面的一个名为"pagedRecords"的attribute中,以供JSP读取。

    adminService.getTotalRecordCount()应不难实现。主要是getRecordsSubset()。Service层的listTable()如下:

        public Collection listTable(String tableName, int firstResult, int maxResults) throws DataAccessException {
            return ((HibernateDao) daoFactory.getDao(tableName)).findByCriteria(firstResult, maxResults);
        }

    Dao层代码:

        public Collection findByCriteria(int firstResult, int maxResults) throws DataAccessException {
            DetachedCriteria criteria = DetachedCriteria.forClass(getEntityClass());
            return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);
        }

    下面看看视图层的使用。

        ......
        <c:forEach items="${pagedRecords.pageList}" var="record">
          ......
        </c:forEach>
        ......

    通过JSTL方便地读出pagedRecords变量的pageList属性。重抄一下上面的RequestPagedListHolder代码相应部分:

        public List getPageList() {
            return recordsSubset;
        }

    返回的正是Hibernate已经为我们检索出来的记录子集。

    下面是图例(参考了NetBeans 5.5 Visual Web Pack的界面设计)。

    图1. 数据库中共有8条记录,每页10行,一页全部显示完毕

    图2. 设为每页5行,显示第0页。尽管共有8条记录,但实际上只检索了5条记录。

    图3. 每页5行时的第1页,这次只检索了3条记录。且pageSize的值在同一个HttpServletSession中自动保存了下来。

    经过这样改进后,代码耦合性大大减小了,也利于以后的扩展。 

    发表于 @ 2008年03月18日 22:21:00|评论(loading...)|编辑

    新一篇: 活用SiteMesh,一个装饰器就可支撑整个网站结构 | 旧一篇: GDI+ for VC6 MFC

    评论

    #java爱好 发表于2008-03-31 17:16:20  IP: 125.70.251.*
    很好,找了很久了,LZ能发一个全一点的例子给我吗?ljnone@yahoo.com.cn,万分感谢~
    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © Sarkuya