1. 程式人生 > >學會改變自己——才能突破

學會改變自己——才能突破

分頁查詢,就是將將過多的結果在有限的介面上分好多頁來顯示,這個是很多網站常用的功能,也是最基本的功能,今天簡單總結一下。

分頁以前聽人們說都是一項技術,但是我覺的不盡然。我認為分頁是將資料庫的資料,利用一些特殊的sql語句來進行查詢,顯示理所應當顯示的內容,更恰當的說可以是對SQL語句的靈活運用,對邏輯思維的簡單使用。

一,一般人們將分頁查詢分為兩類:邏輯分頁,物理分頁,我們先從理論上理解一下:

             1,邏輯分頁概述:就是使用者第一次訪問時,將資料庫的所有記錄全部查詢出來,新增到一個大的集合中,然後存放在session物件,然後通過頁碼計算出當前頁需要顯示的資料內容,儲存到一個小的

list的集合中,並將之儲存到request物件中,跳轉到JSP頁面,進行遍歷顯示。當用戶第二次訪問時,只要不關閉瀏覽器,我們還會從session中獲取資料,來進行顯示。為什麼叫邏輯分頁呢?因為此種方法是在記憶體的session物件中進行計算分頁顯示的,而不是真正的將我們資料庫進行分頁的。

來看它的一些缺點吧:

              a,如果需要查詢的資料量過大,session將耗費大量的記憶體;

              b,因為是在session中獲取資料,如果第二次或者更多此的不關閉瀏覽器訪問,會直接訪問session,從而不能保證資料是最新的。

小結:這種分頁很少使用。但是在資料量小,不會被修改的資料,使用邏輯分頁會提高程式的執行效率。

           2,物理分頁概述:使用資料庫自身所帶的分頁機制,例如,Oracle資料庫的rownum,或者Mysql資料庫中的limit等機制來完成分頁操作。因為是對資料庫實實在在的資料進行分頁條件查詢,所以叫物理分頁。每一次物理分頁都會去連線資料庫。

優點:資料能夠保證最新,由於根據分頁條件會查詢出少量的資料,所以不會佔用太多的記憶體。

缺點:物理分頁使用了資料庫自身帶的機制,所以這樣的SQL語句不通用,導致不能進行資料庫的移植。

小結:在實際中物理分頁還是使用的較多的。

  二,看一下邏輯分頁查詢的應用:

	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);
			
			//轉發
		}
		

}

三,好,物理分頁和邏輯分頁的計算方法差不多,只不過一個是session中一個是在資料庫中,這裡物理分頁總結一下多條件查詢分頁顯示的過程,這裡也將分頁物件進行封裝了:

先看一下分頁物件的編寫:

	/**
	 * 分頁物件
	 * @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;
		}
	}

 有了這個分頁物件,我就可以利用它了,看我們動態引數分頁查詢的過程,重點看註釋步驟:

	/**
	 * 動態引數查詢,難度最大的是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);
			
			//轉發
		     
		}
		
	}

分頁查詢將資料量分成幾批顯示到頁面上。就像我們的書本,這不過這裡的動態,可能因為查詢條件的不同,頁面的內容就不同,所以靈活應用,弄清楚查詢條件,編寫好分頁查詢語句,那麼什麼問題都解決了。