分頁查詢,就是將將過多的結果在有限的界面上分好多頁來顯示,這個是很多網站常用的功能,也是最基本的功能,今天簡單總結一下。
分頁以前聽人們說都是一項技術,但是我覺的不盡然。我認爲分頁是將數據庫的數據,利用一些特殊的sql語句來進行查詢,顯示理所應當顯示的內容,更恰當的說可以是對SQL語句的靈活運用,對邏輯思維的簡單使用。
一,一般人們將分頁查詢分爲兩類:邏輯分頁,物理分頁,我們先從理論上理解一下:
1,邏輯分頁概述:就是用戶第一次訪問時,將數據庫的所有記錄全部查詢出來,添加到一個大的集合中,然後存放在session對象,然後通過頁碼計算出當前頁需要顯示的數據內容,存儲到一個小的list的集合中,並將之存儲到request對象中,跳轉到JSP頁面,進行遍歷顯示。 當用戶第二次訪問時,只要不關閉瀏覽器,我們還會從session中獲取數據,來進行顯示。爲什麼叫邏輯分頁呢?因爲此種方法是在內存的session對象中進行計算分頁顯示的,而不是真正的將我們數據庫進行分頁的。
來看它的一些缺點吧:
a,如果需要查詢的數據量過大,session將耗費大量的內存;
b,因爲是在session中獲取數據,如果第二次或者更多此的不關閉瀏覽器訪問,會直接訪問session,從而不能保證數據是最新的。
小結:這種分頁很少使用。但是在數據量小,不會被修改的數據,使用邏輯分頁會提高程序的執行效率。
2,物理分頁概述:使用數據庫自身所帶的分頁機制,例如,Oracle數據庫的rownum,或者Mysql數據庫中的limit等機制來完成分頁操作。因爲是對數據庫實實在在的數據進行分頁條件查詢,所以叫物理分頁。每一次物理分頁都會去連接數據庫。
優點:數據能夠保證最新,由於根據分頁條件會查詢出少量的數據,所以不會佔用太多的內存。
缺點:物理分頁使用了數據庫自身帶的機制,所以這樣的SQL語句不通用,導致不能進行數據庫的移植。
小結:在實際中物理分頁還是使用的較多的。
二,看一下邏輯分頁查詢的應用:
- <span style="font-size:18px;"> public class PageQueryUserServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //獲取頁碼
- int pageno = Integer.parseInt(request.getParameter("pageno")==null?"1":request.getParameter("pageno"));
- //從session中獲取大List集合
- HttpSession session = request.getSession();
- List<User> bigList = (List<User>)session.getAttribute("bigList");
- //如果第一次訪問
- if(bigList == null){
- //創建大List集合
- bigList = new ArrayList<User>();
- //如果大List集合不存在,則連接數據庫
- Connection conn = null;
- PreparedStatement ps= null;
- ResultSet rs = null;
- try {
- conn = DBUtil.getConnection();
- String sql = "select usercode,username,orgtype from t_user order by regdate desc";
- ps = conn.prepareStatement(sql);
- //執行查詢語句返回查詢結果集
- rs = ps.executeQuery();
- //遍歷結果集封裝javabean對象並存儲到大List集合中
- while(rs.next()){
- User user = new User();
- user.setUsercode(rs.getString("usercode"));
- user.setUsername(rs.getString("username"));
- user.setOrgtype(rs.getString("orgtype"));
- bigList.add(user);
- }
- //將大List集合存儲到session中
- session.setAttribute("bigList", bigList);
- } catch (Exception e) {
- e.printStackTrace();
- } finally{
- DBUtil.close(conn, ps, rs);
- }
- }
- //如果從session中可以獲取到大List集合,則通過頁碼計算得出小List集合
- List<User> smallList = new ArrayList<User>();
- //計算開始標識=頁數大小*(頁碼-1)
- int beginIndex = Const.PAGE_SIZE * (pageno-1);
- //結束標識=頁數大小*頁碼,如果超過了總數據條數,則表示爲最後一頁,寫爲總結條數即可
- int endIndex = Const.PAGE_SIZE * pageno > bigList.size() ? bigList.size() : Const.PAGE_SIZE * pageno;
- for(int i=beginIndex;i<endIndex;i++){
- smallList.add(bigList.get(i));
- }
- //將小List集合存儲到request對象中
- request.setAttribute("userList", smallList);
- //轉發
- }
- }</span>
三,好,物理分頁和邏輯分頁的計算方法差不多,只不過一個是session中一個是在數據庫中,這裏物理分頁總結一下多條件查詢分頁顯示的過程,這裏也將分頁對象進行封裝了:
先看一下分頁對象的編寫:
- <span style="font-size:18px;"> /**
- * 分頁對象
- * @author Administrator
- */
- public class Page<T> {
- /**
- * 頁碼
- */
- private int pageno;
- /**
- * 每頁顯示的記錄條數
- */
- private int pagesize;
- /**
- * 數據集合(需要顯示在網頁中的數據)
- */
- private List<T> dataList;
- /**
- * 總記錄條數
- */
- private int totalsize;
- public Page(String pageno) {
- this.pageno = (pageno == null ? 1 : Integer.parseInt(pageno));
- this.pagesize = Const.PAGE_SIZE;
- this.dataList = new ArrayList<T>();
- }
- public int getPageno(){
- return pageno;
- }
- public int getPagesize(){
- return pagesize;
- }
- public List<T> getDataList(){
- return dataList;
- }
- public void setTotalsize(int totalsize){
- this.totalsize = totalsize;
- }
- public int getTotalsize(){
- return totalsize;
- }
- public int getPagecount(){
- return totalsize%pagesize == 0 ? totalsize/pagesize : totalsize/pagesize + 1;
- }
- /**
- * 通過業務SQL語句獲取分頁SQL語句
- * @param sql 業務SQL
- * @return 分頁SQL語句
- * 這是非常核心的,通過多次嵌套,嵌套出分頁sql語句的編寫
- */
- public String getSql(String sql){
- return "select t1.* from (select t.*,rownum as linenum from ("+sql+") t where rownum<=" + pageno*pagesize + ") t1 where t1.linenum>" + (pageno-1)*pagesize;
- }
- }
- </span>
有了這個分頁對象,我就可以利用它了,看我們動態參數分頁查詢的過程,重點看註釋步驟:
- <span style="font-size:18px;"> /**
- * 動態參數查詢,難度最大的是SQL語句動態拼接。(因爲查詢提交內容不定,查詢提交個數不定)
- * @author Administrator
- */
- public class PageQueryInvServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //解決請求體的中文亂碼問題
- //request.setCharacterEncoding("GB18030");
- //創建分頁對象
- Page<Investor> page = new Page<Investor>(request.getParameter("pageno"));
- //獲取查詢提交的數據
- String invregnum = request.getParameter("invregnum");
- String invname = request.getParameter("invname");
- String startdate = request.getParameter("startdate");
- String enddate = request.getParameter("enddate");
- //拼接業務SQL,注意其中的技巧,where 1=1,另外這裏使用StringBuilder提高拼接的效率
- StringBuilder sql = new StringBuilder("select i.invregnum,i.invname,i.regdate,u.username,i.cty from t_invest i join t_user u on i.usercode=u.usercode where 1=1");
- StringBuilder totalsizeSql = new StringBuilder("select count(*) as totalsize from t_invest i join t_user u on i.usercode=u.usercode where 1=1");
- //創建list集合用來綁定下標和內容,利用的list下標和值對應關係的特點
- List<String> paramList = new ArrayList<String>();
- //動態參數拼接動態SQL語句
- if(StringUtil.isNotEmpty(invregnum)){
- sql.append(" and i.invregnum = ?");
- totalsizeSql.append(" and i.invregnum = ?");
- paramList.add(invregnum);
- }
- if(StringUtil.isNotEmpty(invname)){
- sql.append(" and i.invname like ?");
- totalsizeSql.append(" and i.invname like ?");
- paramList.add("%" + invname + "%");
- }
- if(StringUtil.isNotEmpty(startdate)){
- sql.append(" and i.regdate >= ?");
- totalsizeSql.append(" and i.regdate >= ?");
- paramList.add(startdate);
- }
- if(StringUtil.isNotEmpty(enddate)){
- sql.append(" and i.regdate <= ?");
- totalsizeSql.append(" and i.regdate <= ?");
- paramList.add(enddate);
- }
- //調用獲取分頁SQL
- String pageSql = page.getSql(sql.toString());
- //連接數據庫查詢數據
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- try {
- conn = DBUtil.getConnection();
- ps = conn.prepareStatement(pageSql);
- //給?賦值(重點),這裏list的巧妙使用
- for(int i=0;i<paramList.size();i++){
- ps.setString(i+1, paramList.get(i));
- }
- //執行查詢語句,返回查詢結果集
- rs = ps.executeQuery();
- //遍歷結果集,每遍歷一次,封裝Investor對象,將其添加到List集合中
- while(rs.next()){
- Investor inv = new Investor();
- inv.setInvregnum(rs.getString("invregnum"));
- inv.setInvname(rs.getString("invname"));
- inv.setRegdate(rs.getString("regdate"));
- inv.setUsername(rs.getString("username"));
- inv.setCty(rs.getString("cty"));
- page.getDataList().add(inv);
- }
- //查詢總記錄條數,並且設置到分頁對象中
- ps = conn.prepareStatement(totalsizeSql.toString());
- //給?賦值
- for(int i=0;i<paramList.size();i++){
- ps.setString(i+1, paramList.get(i));
- }
- rs = ps.executeQuery();
- if(rs.next()){
- page.setTotalsize(rs.getInt("totalsize"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally{
- DBUtil.close(conn, ps, rs);
- }
- //將分頁對象存儲到request範圍中
- request.setAttribute("pageObj", page);
- //轉發
- }
- }
- </span>
分頁查詢將數據量分成幾批顯示到頁面上。就像我們的書本,這不過這裏的動態,可能因爲查詢條件的不同,頁面的內容就不同,所以靈活應用,弄清楚查詢條件,編寫好分頁查詢語句,那麼什麼問題都解決了。