【问题标题】:ResultSet to Pagination结果集到分页
【发布时间】:2010-12-31 12:21:38
【问题描述】:

如何将Resultset 对象转换为JSP 上的分页视图?

例如,这是我的查询和结果集:

pst = con.prepareStatement("select userName, job, place from contact");
rs = pst.executeQuery();

【问题讨论】:

    标签: java jsp jdbc pagination


    【解决方案1】:

    首先,您需要向 JSP 添加一两个额外的请求参数:firstrow 和(可选)rowcountrowcount 也可以省略,完全在服务器端定义。

    然后在 JSP 中添加一堆分页按钮:next 按钮应该指示Servletfirstrow 的值增加为rowcountprevious 按钮显然应该将firstrow 的值减少为rowcount 的值。不要忘记正确处理负值和溢出!您可以在 SELECT count(id) 的帮助下完成。

    然后触发特定的 SQL 查询以检索结果的子列表。然而,确切的 SQL 语法取决于所使用的数据库。在 MySQL 和 PostgreSQL 中,使用 LIMITOFFSET 子句很容易:

    private static final String SQL_SUBLIST = "SELECT id, username, job, place FROM"
        + " contact ORDER BY id LIMIT %d OFFSET %d";
    
    public List<Contact> list(int firstrow, int rowcount) {
        String sql = String.format(SQL_SUBLIST, firstrow, rowcount);
    
        // Implement JDBC.
        return contacts;
    }
    

    在 Oracle 中,您需要一个带有 rownum 子句的子查询,该子句应如下所示:

    private static final String SQL_SUBLIST = "SELECT id, username, job, place FROM"
        + " (SELECT id, username, job, place FROM contact ORDER BY id)"
        + " WHERE ROWNUM BETWEEN %d AND %d";
    
    public List<Contact> list(int firstrow, int rowcount) {
        String sql = String.format(SQL_SUBLIST, firstrow, firstrow + rowcount);
    
        // Implement JDBC.
        return contacts;
    }
    

    在 DB2 中,您需要 OLAP 函数 row_number()

    private static final String SQL_SUBLIST = "SELECT id, username, job, place FROM"
        + " (SELECT row_number() OVER (ORDER BY id) AS row, id, username, job, place"
        + " FROM contact) AS temp WHERE row BETWEEN %d AND %d";
    
    public List<Contact> list(int firstrow, int rowcount) {
        String sql = String.format(SQL_SUBLIST, firstrow, firstrow + rowcount);
    
        // Implement JDBC.
        return contacts;
    }
    

    我不使用 MSSQL,但它在语法上类似于 DB2。另见this topic

    最后,只需使用 JSTL c:forEach 以通常的方式在 JSP 页面中显示子列表。

    <table>
        <c:forEach items="${contacts}" var="contact">
            <tr>
                <td>${contact.username}</td>
                <td>${contact.job}</td>
                <td>${contact.place}</td>
            </tr>
        </c:forEach>
    </table>
    <form action="yourservlet" method="post">
        <input type="hidden" name="firstrow" value="${firstrow}">
        <input type="hidden" name="rowcount" value="${rowcount}">
        <input type="submit" name="page" value="next">
        <input type="submit" name="page" value="previous">
    </form>
    

    请注意,有些可能建议您需要SELECT整个表并将List&lt;Contact&gt;保存在会话范围内,并使用List#subList()进行分页。但这与具有数千行和多个并发用户的内存效率相比相去甚远

    对于那些对使用 h:dataTable 组件的 JSF/MySQL 上下文中的类似答案感兴趣的人,您可能会发现 this article 很有用。它还包含一些有用的与语言无关的数学,以使“类似 Google”的分页很好地工作。

    【讨论】:

    • 假设在用户查看一页时发生了数据库插入和删除。我想,行号会随着时间的推移而不稳定吗?效果可能是给用户一些意想不到的位置变化。
    • 这无关紧要。您不想查看已删除的项目或错过编辑的数据。唯一的办法就是将整个 DB 表拖入 Java 的内存并只处理它,但您不想这样做。
    • @BalusC - 哇。很棒的答案。我自己可以好好利用它。
    • BalusC,这是一个很好的编译但是Oracle sql 是错误的。我在我的答案中写了正确的 sql。
    • 您的 Oracle 查询不起作用。如果下限 > 1,则不能将 BETWEEN 与 ROWNUM 一起使用,因为每一行将在 为其分配 rownum 值之前被过滤。所以“where ROWNUM BETWEEN 2 and xxx”总是返回一个空的结果集。见stackoverflow.com/a/13740166/191367
    【解决方案2】:

    这个 Oracle 示例是错误的。

    是的,在外部选择中 ROWNUM 值很好,但它仍然是伪列,所以我们不能在它上面使用 BETWEEN。我们还需要一个选择。

    正确的sql代码是:

    SELECT c.*
      FROM (SELECT c.*, ROWNUM as rnum
              FROM (SELECT id, username, job, place FROM contact ORDER BY id) c) c
     WHERE c.rnum BETWEEN 5 AND 10
    

    同志们,使用实心sql字符串和Statement类是SLOOOW。每次执行时,Oracle 都必须解析您的 SQL。

    //Slooow example
    Satement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery("select * from my_table where id = 11");
    

    使用 PreparedStatement 和绑定参数。

       //Faster example
       PreparedStatement ps = conn.getPrepareStatement("select * from my_table where id = ?");
       ps.setInt(1, 11);
    

    最快的解决方案是将你的 sql 放入 oracle 存储过程中并使用 CallableStatement 来调用它。

    //Fastest example
    CallableStatement cs = conn.prepareCall("{? = call my_plsql_function(?)}");
    cs.setInt(1, 11);
    

    【讨论】:

      【解决方案3】:

      您可以做以下几件事:

      • 将结果集编组到某个对象/记录列表
      • 根据您所需的页面大小,根据结果集确定您将拥有多少页。
      • 检查所需页面的请求参数,并根据页面上显示的项目数检查偏移量。因此,如果您在第 4 页上显示 12,则您的偏移量为 48。
      • 根据项目数确定总页数。

      • 根据您确定的偏移量显示您的项目(仅从项目 48 开始显示)

      • 根据您确定的总页数使用页数生成分页。

      =======

      这是您的基本方法。您可以使用以下方法进行调整:

      • 确定将查询限制在页面的方法(但这无助于确定页面大小)
      • 花哨的分页方式
      • 等等..

      【讨论】:

      • 如果您有一个包含数千行的结果集,这不是很有效。而是在数据库级别进行分页。
      【解决方案4】:

      您可以使用 displaytag 进行分页或结果集,但您可以从 displattag 下载一些 jar 文件

      首先创建一个 servlet StudentList.java

      public class StudentList extends HttpServlet 
      

      { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

              ArrayList al=new ArrayList();
              StudentDao stdo=new StudentDao(); // this is DAO Class (Data Acccess Object)
      
              try
              {
                  al=stdo.getStudentList(); //getstudent list dao method
              }
              catch (SQLException e) 
              {
                  e.printStackTrace();
              }
              catch (Exception e) 
              {
                  e.printStackTrace();
              }
              request.setAttribute("al",al);
      
              RequestDispatcher rd=request.getRequestDispatcher("StudentPaging.jsp");
              rd.forward(request,response);
      
      }
      

      }

      // dao方法

      public ArrayList getStudentList() throws SQLException,Exception
      {
          ArrayList ai=new ArrayList();
          Connection con=null;
          Statement st=null;
          ResultSet rs=null;
          Date dt=new Date();
          SimpleDateFormat sdf=new SimpleDateFormat("dd/MM/yyyy");
          StudentInformation sdata=null;
      
          con=MyConnection.creatConnection();
          if(con!=null)
          {
              st=con.createStatement();
              String select="select * from STUDENT";
              System.out.println(select);
      
              rs=st.executeQuery(select);
              if(rs!=null)
              {
                  while(rs.next())
                  {
                      sdata=new StudentInformation();
                      sdata.setSid(rs.getString("SID"));
                      sdata.setFirstName(rs.getString("FIRSTNAME"));
                      sdata.setMiddleName(rs.getString("MIDDLENAME"));
                      sdata.setLastName(rs.getString("LASTNAME"));
                      dt=rs.getDate("SDATE");
                      sdata.setDateofbirth(sdf.format(dt));
                      sdata.setGender(rs.getString("GENDER"));
                      sdata.setAddress(rs.getString("ADDRESS"));
                      sdata.setHigestQulification(rs.getString("HIQULIFICATION"));
                      sdata.setLanguageKnow(rs.getString("LANGUAGE"));
                      sdata.setHobby(rs.getString("HOBBY"));
                      sdata.setTermCondition(rs.getString("TERMCON"));
                      ai.add(sdata);
                  }
              }
          }
          return ai;
      }
      

      【讨论】:

        【解决方案5】:

        查找值列表模式并应用它。这通常是处理这类事情的最佳方式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-23
          • 2018-10-25
          • 2013-05-13
          • 1970-01-01
          相关资源
          最近更新 更多