• 基于JDBC的通用分页实现 (转)


    (Hibernate的分页做得很好很强大,用的人都知道,这个就不用再说了)
    1.为什么要分页?
    首先是数据量太大会影响查询和传输的性能,关键是对用户来说一下看到数万条记录也不是那么友好。
    2.有哪些分页技术?
    a)存储过程分页
    在数据库中创建一个存储过程,传入SQL和页码获得当前页的记录。需要对具体数据库的语法相当熟悉才能够编写,当然也可以直接google。性能最好,但不跨数据库平台。
    b)数据库专有sql特性分页
    使用数据库专有的特性(MSSQL的top、Oracle的rownum、MySQL的limit等)实现当前页记录提取。性能也非常好,但也不跨数据库平台。
    (为什么非要跨数据库平台呢?好吧,如果你的项目确定是不会换数据库的那就这么写吧。)
    c)纯JDBC分页
    通过Statement的setMaxRow(endIndex)和rs.absoulte(beginIndex)仅取得当前页范围内的记录。此种方式的性能依赖于厂商对JDBC规范的实现,如果厂商的rs读取是以流的形式进行的,性能还是有所保障的(好吧我承认是这不负责的说法)。这种方式的通用性是最好的,完全与数据库平台无关了(牺牲性能换取跨平台特性是Java平台的常用手法,嘿嘿嘿)。
    d)根据数据库类型自动生成数据库专有特性的sql语句 
    其实这就是Hibernate的实现方法,如果你觉得自己分析SQL语法然后将SQL转换为特定数据库语法比较麻烦,那就用Hibernate吧。如果你不想用Hibernate还是想用这种方法,那就麻烦你多辛苦一点,用力写出来给大家分享吧。
    我们来理一理分页的逻辑吧
    首先,对用户而言他是不关心你是怎么分页的,他只要给你一个页码,然后你把那一页的记录给他就行了。
    为了能够取得指定页码所对应的记录,我们还需要两个关键的参数:每页记录数、总记录数。
    每页记录数可以设个默认值,20、30都行吧。
    总记录数就需要额外从数据库中取得了,这个也是没办法的事,谁让ResultSet没提供获取记录总数的方法呢。
    通过这两个参数能够计算出来总页数,同时也就可以判断用户给出的页码是否有效了(超出最后一页或者小于第一页),然后根据页码和每页记录数就可以算出来当前页的记录起止范围了。(这些个算术都是小学的,就不用说了吧,其实我也是想了半天的)
    为了表示方便,就把这些参数写成一个类,同时把计算方法也写进去
    Java代码 
    /** 
    * 
    * @author Lixor(at)live.cn 
    * 
    */ 
    public class Page { 
    private int rowTotal;// 总记录数 
    private int pageSize = 10;// 每页记录数 
    private int count;// 当前页码 
    private int total;// 总页数 
    private int beginIndex;//起始记录下标 
    private int endIndex;//截止记录下标 
    /** 
    * 使用总记录数、当前页码构造 
    * 
    * @param rowTotal 
    * @param count 
    * 页码,从1开始 
    */ 
    public Page(int totalRow, int count) { 
    this.rowTotal = totalRow; 
    this.count = count; 
    calculate(); 
    } 
    /** 
    * 使用总记录数、当前页码和每页记录数构造 
    * 
    * @param rowTotal 
    * @param count 
    * 页码,从1开始 
    * @param pageSize 
    * 默认30条 
    */ 
    public Page(int totalRow, int count, int pageSize) { 
    this.rowTotal = totalRow; 
    this.count = count; 
    this.pageSize = pageSize; 
    calculate(); 
    } 
    private void calculate() { 
    total = rowTotal / pageSize + ((rowTotal % pageSize) > 0 ? 1 : 0); 
    if (count > total) { 
    count = total; 
    } else if (count < 1) { 
    count = 1; 
    } 
    beginIndex = (count - 1) * pageSize ; 
    endIndex = beginIndex + pageSize ; 
    if (endIndex > rowTotal) { 
    endIndex = rowTotal; 
    } 
    } 
    public int getCount() { 
    return count; 
    } 
    public int getTotal() { 
    return total; 
    } 
    public int getTotalRow() { 
    return rowTotal; 
    } 
    public int getPageSize() { 
    return pageSize; 
    } 
    public int getBeginIndex() { 
    return beginIndex; 
    } 
    public int getEndIndex() { 
    return endIndex; 
    } 
    } 

    }


    好了,可别散扯了,直接来个纯JDBC分页的示例吧
    Java代码 
    //从页面取得页码 
    int pageCount = 1; 
    try { 
    pageCount = Integer.parseInt(request.getParameter("page.count")); 
    } catch (Exception ex) {} 
    //取得总记录数,创建Page对象 
    int totalRow = productDao.getProductAmount();//通过select count 取得总记录数 
    Page page = new Page(totalRow, pageCount); 
    productDao.list(page); 


    ProductDao.java
    Java代码 
    public List<Product> list(Page page) { 
    List<Product> productList = new ArrayList<Product>(); 
    try { 
    String sql = "SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id"; 
    Connection conn = null; 
    try { 
    conn = DbUtil.getConnection(); 
    Statement st = conn.createStatement(); 
    st.setMaxRows(page.getEndIndex());//关键代码,设置最大记录数为当前页记录的截止下标 
    ResultSet rs = st.executeQuery(sql); 
    if (page.getBeginIndex() > 0) { 
    rs.absolute(page.getBeginIndex());//关键代码,直接移动游标为当前页起始记录处 
    } 
    while (rs.next()) { 
    Product product = new Product(); 
    …… 
    productList.add(product); 
    } 
    rs.close(); 
    st.close(); 
    } finally { 
    if (conn != null) { 
    conn.close(); 
    } 
    } 
    } catch (SQLException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } 
    return productList; 
    } 


    最后我试了一下在MySQL上产品表里有10W数据时,执行的日志大概是
    Java代码 
    第1页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (0 milliseconds) 
    第3页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (0 milliseconds) 
    第5页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (0 milliseconds) 
    第10页 [http-8080-1] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (3 milliseconds) 
    第30页 [http-8080-1] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (6 milliseconds) 
    第100页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (15 milliseconds) 
    第1000页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (152 milliseconds) 
    第10000页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id; (2030 milliseconds) 
    很显然纯JDBC分页的性能与页码有关(需要查询的数据范围,也就是说st.setMaxRow是有效的),越往后翻,性能越差。
    不过话说回来没有哪个用户去无聊的自己一页一页翻到第10000页去,一般都是翻两页找不到那就会进一步缩小查询条件的范围了。
    所以折衷的办法就是一方面页面记录数搞大一点,另外就是限制用户翻页的范围,超过10页就直接提示用户缩小查询范围,如果这样的话纯JDBC分页也就可以凑和着用了。

  • 相关阅读:
    数据库连接池实现
    Linux array_vpnc
    MVC小结
    Linux和Windows下 classpath 的差异
    无法删除DLL文件解决方法(转)
    电信工程管理方法
    常用设计思想
    MAX262使用说明
    基于FPGA的FIR滤波器(草稿)
    数字存储示波器(草稿)
  • 原文地址:https://www.cnblogs.com/licomeback/p/3030414.html
Copyright © 2020-2023  润新知