最近在學習JavaWeb的時候,用到了分頁功能,現在進行一個記錄,以備不時之需
第一步:先完成PageBean的編寫
就是對當前頁數,每頁顯示的記錄數,總記錄數,總頁數,分頁顯示的信息進行封裝。作為通用的分頁功能的實現,這里用到了泛型
import java.util.List; /** * 分頁封裝 * */ public class PageBean<T> { private int currPage;//當前頁數 private int pageSize;//每頁顯示記錄數 private int totalCount;//總記錄數 private int totalPage;//總頁數 private List<T> list;//每頁顯示的信息 public int getCurrPage() { return currPage; } public void setCurrPage(int currPage) { this.currPage = currPage; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public int getTotalPage() { return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public List<T> getList() { return list; } public void setList(List<T> list) { this.list = list; } }
第二步:在Action類中編寫一個分頁方法
Action中通過調用業務層Service類的分頁方法,Employee就是具體的信息Bean,之前泛型的使用就在於此,在不同項目中使用不同的信息Bean可以完成多種類信息的分頁,employeeService就是具體的業務層Service,Service中我們也有一個叫findAll的方法。這里的currPage生成其set/get方法,等用戶點擊了頁面及具體的頁數,Struts2會獲得具體的頁數,然后將currPage傳給Service
// 分頁(當前頁)這里等於1是為了使第一頁為默認頁 private int currPage = 1; public int getCurrPage() { return currPage; } public void setCurrPage(int currPage) { this.currPage = currPage; } /** * 分頁查詢 */ public String findAll() { PageBean<Employee> pageBean = employeeService.findAll(currPage); ActionContext.getContext().getValueStack().push(pageBean); return "findAll"; }
第三步:編寫Service中的分頁方法
先生成一個PageBean對象(注意泛型),之后開始封裝這個Bean。pageSize(每頁顯示的記錄數)這里設置為3,就是每頁顯示3條記錄,totalCount(總記錄數)通過Dao中的findCount()方法來查到,totalPage(總頁數)=totalCount(總記錄數)/pageSize(每頁大小),Math.ceil可以獲得一個double的近似值(大於等於),之后我們通過Double包裝類的intValue再轉成int型,begin(每一頁的開頭的序號),list的數據也通過Dao的findPage(int,int)方法獲得
public PageBean<Employee> findAll(int currPage) { PageBean<Employee> pageBean = new PageBean<>(); // 封裝pageBean pageBean.setCurrPage(currPage); int pageSize = 3; pageBean.setPageSize(pageSize); int totalCount = employeeDao.findCount(); pageBean.setTotalCount(totalCount); Double totalPage = Math.ceil((double) totalCount / pageSize); pageBean.setTotalPage(totalPage.intValue()); int begin = (currPage - 1) * pageSize; List<Employee> list = employeeDao.findPage(begin, pageSize); pageBean.setList(list); return pageBean; }
第四步:編寫Dao中的方法
這之前在Service中使用的findCount()和findPage(int,int),值得注意的就是findCount()中的hql語句使用count(*),還有就是findPage(int,int)中使用了org.hibernate.criterion.DetachedCriteria類,查詢時不是用find方法,而是findByCriteria來查詢
/** * 查詢總記錄數 */ public int findCount() { String hql="select count(*) from Employee"; List<Long> list = (List<Long>) hibernateTemplate.find(hql); if(list.size()>0){ return list.get(0).intValue(); } return 0; } /** * 分頁信息 */ public List<Employee> findPage(int begin, int pageSize) { DetachedCriteria criteria=DetachedCriteria.forClass(Employee.class); List<Employee> list = (List<Employee>) hibernateTemplate.findByCriteria(criteria, begin, pageSize); return list; }
基本上通過以上四步就完成了分頁功能邏輯代碼的編寫,接下來就是對視圖層頁面的編寫
下面是個分頁的簡單顯示,處於首頁時,只顯示下一頁和尾頁,處於尾頁時,只顯示首頁和上一頁,其他頁就都顯示。這里使用的Struts2的標簽庫,所以不要忘了要加上
<%@ taglib uri="/struts-tags" prefix="s" %>
為什么我們可以直接currPage,totalPage等屬性?是因為我們之前將PageBean對象放入了值棧中
<table border="0" cellspacing="0" cellpadding="0" width="900px"> <tr> <td align="right"> <span>第<s:property value="currPage"/>/<s:property value="totalPage"/>頁</span> <span>總記錄數:<s:property value="totalCount"/>/每頁顯示:<s:property value="pageSize"/>條</span> <span> <s:if test="currPage!=1"> <a href="department_findAll.action?currPage=1">[首頁]</a> <a href="department_findAll.action?currPage=<s:property value="currPage-1"/>">[上一頁]</a> </s:if> <s:if test="currPage!=totalPage"> <a href="department_findAll.action?currPage=<s:property value="currPage+1"/>">[下一頁]</a> <a href="department_findAll.action?currPage=<s:property value="totalPage"/>">[尾頁]</a> </s:if> </span> </td> </tr> </table>
效果圖:
可以使用了struts2的<s:iterator>標簽來進行迭代顯示數據,這里給個參考
<s:iterator value="list" var="d"> <tr> <td align="center"><s:property value="#d.dname"/></td> <td align="center"><a href="">編輯</a></td> <td align="center"><a href="">刪除</a></td> </tr> </s:iterator>
注意:一定要在Struts.xml文件中配置轉跳到我們編寫的action上(在這里是department_findAll.action),不然打開要分頁的頁面時,不會進行分頁操作,只有你選擇了頁數才會分頁
后記
我在使用的過程中發現,要使用分頁功能的地方不少,在某些地方,使用上面的會出錯:例如對條件查詢之后的結果進行分頁,第一頁很OK,但是你點擊下一頁/某一頁的時候會出現查詢條件的丟失問題,再進行的查詢結果分頁是沒有條件的,就會出錯
解決辦法(我使用Servlet+JDBC實現的,原理是一樣的):
在我們的POJO中添加一個url屬性,表示查詢條件,因為POST方式條件是放在請求頭中的,很不方便,所以再進行條件查詢的的表單使用GET方式:
<form action="<c:url value='/customerServlet'/>" method="get">
我們需要獲得這個url(包括項目名+Servlet名+條件),獲得這個url封裝到POJO中
改進后的PageBean:
import java.util.List; public class PageBean<T> { //當前頁 private int currPage; //每頁記錄數 private int pageSize; //總記錄數 private int totalCount; //數據集合 private List<T> list; //url表示條件查詢的條件(GET方式) private String url; public int getCurrPage() { return currPage; } public void setCurrPage(int currPage) { this.currPage = currPage; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } //設置總頁數(計算得出) public int getTotalPage() { Double totalPage=Math.ceil((double)totalCount/pageSize); return totalPage.intValue(); } public List<T> getList() { return list; } public void setList(List<T> list) { this.list = list; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
控制層要添加的方法(encoding()方法根據情況添加):
/** * 得到包含查詢的條件URL */ private String getURL(HttpServletRequest request){ String contextPath=request.getContextPath(); //項目名 String servletPath=request.getServletPath(); //servlet路徑,即/*Servlet 建議使用request.getRequestURI()獲得一個包含項目名+Servlet的路徑 String queryString=request.getQueryString(); //?后面的參數 //判斷參數中是否帶當前頁(currPage) if(queryString.contains("&currPage=")){ int index=queryString.lastIndexOf("&currPage="); queryString=queryString.substring(0, index); } return contextPath+servletPath+"?"+queryString; } /** *對(這里是4個條件)條件進行編碼(GET方式防止中文亂碼,POST方式已經在BaseServlet中配置) * @throws UnsupportedEncodingException * */ private Customer encoding(Customer customer) throws UnsupportedEncodingException{ String cname=customer.getCname(); String gender=customer.getGender(); String cellPhone=customer.getCellphone(); String email=customer.getEmail(); if(cname!=null&&!cname.isEmpty()){ // cname=new String(cname.getBytes("ISO-8859-1"),"UTF-8"); //tomcat8之前tomcat默認字符編碼使用是ISO-8859-1 cname=new String(cname.getBytes(),"UTF-8"); customer.setCname(cname); } if(gender!=null&&!gender.isEmpty()){ //gender=new String(gender.getBytes("ISO-8859-1"),"UTF-8"); gender=new String(gender.getBytes("ISO-8859-1"),"UTF-8"); customer.setCname(gender); } if(cellPhone!=null&&!cellPhone.isEmpty()){ //cellPhone=new String(cellPhone.getBytes("ISO-8859-1"),"UTF-8"); cellPhone=new String(cellPhone.getBytes(),"UTF-8"); customer.setCname(cellPhone); } if(email!=null&&!email.isEmpty()){ //email=new String(email.getBytes("ISO-8859-1"),"UTF-8"); email=new String(email.getBytes(),"UTF-8"); customer.setCname(email); } return customer; }
這個geturl()方法就是獲得url的方法,我用來表示當前頁的變量使用的是currPage,根據實際情況更換。encoding()方法是處理get方式獲得的條件中文亂碼問題,如果項目使用tomcat8之后的服務器就不用轉碼了。注意:現在是所有的查詢的方法的需要使用geturl()方法,並將返回值封裝到POJO中。使用request.getRequestURI()方法也可以獲得Struts2中Action的地址。使用框架開發應該就不用考慮encoding()這個編碼方法,只需要處理geturl()方法了
多條件查詢的Servlet方法:
/** * 多條件查詢 */ public String query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲得查詢條件 Customer customer=CommonUtils.toBean(request.getParameterMap(), Customer.class); //處理GET請求的編碼格式 //customer = encoding(customer); 因為我的tomcat是8.5,所以就不轉碼了 //處理PageBean int pageSize=10; //每頁記錄數為10 int currPage=getCurrentPage(request); int begin=(currPage-1)*pageSize; //計算每頁的開頭 PageBean<Customer> pageBean=customerService.query(customer,begin,pageSize); //封裝當前頁和每頁記錄數 pageBean.setCurrPage(currPage); pageBean.setPageSize(pageSize); //封裝url pageBean.setUrl(getURL(request)); //將pageBean保存request request.setAttribute("pb", pageBean); return "f:/list.jsp"; }
注意:因為這種方式的頁碼算是使用的超連接,所以currPage可能會被惡意輸入,需要驗證一下,這里粘一個使用正則驗證是否是數字的方法(其實不僅僅要驗證是否為數字,還應該驗證當前頁不要大於總頁數,也不要小於1):
/** * 使用正則驗證輸入的是否為數字 * @param string * @return */ public static boolean isNum(String string) { Pattern pattern=Pattern.compile("[1-9]{1}\\d*"); Matcher matcher = pattern.matcher(string); return matcher.matches(); }
Dao中多條件查詢:
/** * 多條件查詢 * */ public PageBean<Customer> query(Customer customer, int begin, int pageSize) { try { PageBean<Customer> pageBean=new PageBean<Customer>(); /** * 根據條件查詢出總記錄數 * 拼sql語句 */ StringBuilder cntSql=new StringBuilder("select count(*) from t_customer"); StringBuilder whereSql=new StringBuilder(" where 1=1"); List<Object> params=new ArrayList<Object>(); String cname=customer.getCname(); if(cname!=null&&!cname.trim().isEmpty()){ whereSql.append(" and cname like ?"); params.add("%"+cname+"%"); } String gender=customer.getGender();if(gender!=null&&!gender.trim().isEmpty()){ whereSql.append(" and gender=?"); params.add(gender); } String phone=customer.getCellphone(); if(phone!=null&&!phone.trim().isEmpty()){ whereSql.append(" and cellphone like ?"); params.add("%"+phone+"%"); } String email=customer.getEmail(); if(email!=null&&!email.trim().isEmpty()){ whereSql.append(" and email like ?"); params.add("%"+email+"%"); } //運行sql Number count=(Number) qr.query(cntSql.append(whereSql).toString(), new ScalarHandler(),params.toArray()); pageBean.setTotalCount(count.intValue()); /** * 根據條件查詢出每頁數據 */ StringBuilder listSql=new StringBuilder("select * from t_customer"); //limit子句分頁 StringBuilder limitSql=new StringBuilder(" order by cname limit ?,?"); //添加這兩個參數 params.add(begin); params.add(pageSize); //運行sql List<Customer> beanList=qr.query(listSql.append(whereSql).append(limitSql).toString(), new BeanListHandler<Customer>(Customer.class), params.toArray()); pageBean.setList(beanList); return pageBean; } catch (Exception e) { e.printStackTrace(); } return null; }
我還是使用的是拼接sql的方式,與眾不同的是分頁中使用的count語句limit語句都是帶where條件的
在頁面中也需要進行相應的修改(我這里使用的是JSTL):
第${pb.currPage }頁/共${pb.totalPage }頁 <c:if test="${pb.currPage>1 }"> <a href="${pb.url }&currPage=1">首頁</a> <a href="${pb.url }&currPage=${pb.currPage-1 }">上一頁</a> </c:if>
<c:if test="${pb.currPage<pb.totalPage }"> <a href="${pb.url }&currPage=${pb.currPage+1 }">下一頁</a> <a href="${pb.url }&currPage=${pb.totalPage }">尾頁</a> </c:if>
pb就是我放在request中的pageBean的名字,注意url需要我們從pageBean的url中取出,頁面只關心當前頁,不去關心路徑到底是什么
一個頁碼的實現
可能說一個頁碼又什么好實現的,直接把所有頁數寫出來,都加上超鏈接不就好了,但是觀察百度等有頁碼的網站,你會發現頁面是會隨着頁數變換的,比如說:先顯示10頁,但是當你點擊第7頁的時候,第1頁應該隱藏,第11頁應該出現,只有出現11頁我們才能點擊,這就是要實現的功能
<%-- 頁碼 --%> <c:choose> <%-- 總頁數小於等於10頁,把所有頁顯示 --%> <c:when test="${pb.totalPage<=10 }"> <c:set var="begin" value="1"/> <c:set var="end" value="${pb.totalPage }"/> </c:when> <c:otherwise> <%--總頁數>10,計算begin和end --%> <c:set var="begin" value="${pb.currPage-5 }"/> <c:set var="end" value="${pb.currPage+4 }"/> <%--處理頭溢出 --%> <c:if test="${begin<1 }"> <c:set var="begin" value="1"/> <c:set var="end" value="10"/> </c:if> <%-- 處理尾溢出 --%> <c:if test="${end>pb.totalPage }"> <c:set var="begin" value="${pb.totalPage-9 }"/> <c:set var="end" value="${pb.totalPage }"/> </c:if> </c:otherwise> </c:choose> <c:forEach begin="${begin }" end="${end }" var="i"> <c:choose> <c:when test="${i eq pb.currPage }"> [${i }] </c:when> <c:otherwise> <a href="${pb.url }&currPage=${i }">[${i }]</a> </c:otherwise> </c:choose> </c:forEach>
這是一個顯示10頁,點擊第7頁后,1頁消失11頁出現,點擊8頁后,2頁消失12頁出現,以此類推
效果圖(這是我把頁碼放在上一頁和下一頁之間的效果):