技術涵蓋(JavaWeb、HTML、Ajax、JQuery、Bootstrap )
我們接觸 JavaWeb 這一部分知識的時候,我們經常會做一些小Demo來練手,不可避免的就需要接觸到一定量的數據,我們常常需要將數據從數據庫中回顯到頁面中,但是隨着數據量的增加,如果不對數據的查詢或者顯示進行一定的處理,那麼會出現各式各樣的問題,例如:
- 客戶端:如果數據同時展示在一個頁面中,用戶體驗效果比較差,操作也是極其不方便
- 服務端:一次請求,查詢到所有的數據,數據傳輸量過大或導致超時或者響應速度變慢,對於服務器的負荷過大
分頁方式
前端 JS 分頁 - 不推薦
我們可以請求獲取到所有數據後,使用 JavaScript 來進行數據分頁顯示,單純的在數據的顯示這一方面看確實美觀了很多,並且這種分頁方式要比後端分頁簡單很多
但是如果存在一定數據量的情況下,這種方式着實有一些尷尬,他並沒有解決了我們服務端的任何問題,反而會讓用戶在等待響應數據耗時過多,體驗不佳,不過它仍然是一種分頁方式
在這裏我們重點講解後端分頁,所以我們簡單的演示一下,也把代碼貼出來,由於我們 html 中使用的是 BootStrap 前端框架,所以我們藉助了 bootstrap-table 這個前端分頁插件
前端 JS 分頁 演示代碼:
![11.1-01-003](G:\公衆號\markdown文件\11-分頁與條件查詢\分頁查詢\11.1-01-003.png)<!DOCTYPE html>
<!-- 網頁使用的語言 -->
<html lang="zh-CN">
<head>
<!-- 指定字符集 -->
<meta charset="utf-8">
<!-- 使用Edge最新的瀏覽器的渲染方式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- viewport視口:網頁可以根據設置的寬度自動進行適配,在瀏覽器的內部虛擬一個容器,容器的寬度與設備的寬度相同。
width: 默認寬度與設備的寬度相同
initial-scale: 初始的縮放比,爲1:1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3個meta標籤*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
<title>用戶信息管理系統</title>
<!-- 導入CSS的全局樣式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!--導入表格插件樣式表-->
<link href="css/bootstrap-table.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h3 style="text-align: center">用戶信息列表</h3>
<!--存放工具欄-->
<div id="toolbar"></div>
<!--存放生成的表格-->
<table id="userInfo_table" class="table table-hover">
</table>
</div>
<!-- jQuery導入,建議使用1.9以上的版本 -->
<script src="js/jquery-2.1.0.min.js"></script>
<!-- 導入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
<!--導入表格插件-->
<script src="js/bootstrap-table.min.js"></script>
<style type="text/css">
td, th {
text-align: center;
}
</style>
<script>
$(function () {
$("#userInfo_table").bootstrapTable({
url: 'user/userList',
toolbar: '#toolbar',
method: 'GET',
striped: true, //是否顯示行間隔色
cache: false, //是否使用緩存
toolbarAlign: "right", //工具欄對齊方式
sidePagination: "client", //分頁方式:client客戶端分頁,server服務端分頁
search: true, //是否顯示錶格搜索,此搜索是客戶端搜索,不會進服務端
uniqueId: "id",
pageNumber: 1, //初始化加載第一頁
pageSize: 10, //每頁的記錄行數
pageList: [5, 10, 15, 20], //可供選擇的每頁的行數
pagination: true, // 是否分頁
sortable: true, // 是否啓用排序
sortOrder: "asc", //排序方式
showColumns: true, //是否顯示列選擇按鈕
showRefresh: true, //是否顯示刷新按鈕
clickToSelect: true, //是否啓用點擊選中行
// height: 500, //行高
showToggle: true, //是否顯示詳細視圖和列表視圖的切換按鈕
cardView: false, //是否顯示詳細視圖
detailView: false, //是否顯示父子表
queryParamsType: '',//設置請求參數格式
queryParams: function queryParams(params) { //設自定義查詢參數
/*請求遠程數據時,可以通過修改queryParams來發送其他參數。
如果queryParamsType = 'limit',params對象包含:limit,offset,search,sort,order。
否則,它包含:pageSize,pageNumber,searchText,sortName,sortOrder。
返回false停止請求。
默認: function(params) { return params }*/
return params;
},
columns: [{
title: "全選",
field: "select",
checkbox: true,
width: 20, //寬度
align: "center", //水平
valign: "middle" //垂直
}, {
field: 'uid',
title: '編號'
}, {
field: 'username',
title: '用戶名'
}, {
field: 'nickname',
title: '暱稱'
}, {
field: 'email',
title: '郵箱'
}, {
field: 'telephone',
title: '電話'
}, {
field: 'gender',
title: '性別'
}, {
field: 'birthday',
title: '生日'
},{
field: 'id',
title: '操作',
// width: 120,
align: 'center',
valign: 'middle',
formatter: actionFormatter
}]
})
})
//操作欄的格式化
function actionFormatter(value, row, index) {
var id = row.id;
var result = "";
result += "<button style='cursor: pointer;margin-right: 5px' class='btn btn-primary' title='修改' οnclick=''>修改</button>";
result += "<button style='cursor: pointer' class='btn btn-primary' title='刪除' οnclick=''>刪除</button>";
return result;
}
</script>
</body>
</html>
(二) 後端分頁 - 推薦
後端分頁與前端分頁的最大不同就是,它不需要一次性向後端請求大量的數據,而是根據用戶的設定,一次請求一定量的數據,然後將這些數據回顯到頁面上,後端分頁也纔是分頁的正確打開方式,其避免了一次性從數據庫獲取很多數據,也可以美化前端展示效果,優化用戶體驗
後端分頁的實現方式
(一) 整體分析
根據我們上面所講的,我們需要的就是前端向後端提交請求,後端響應前端需要的數據,並且展示在前端頁面中
前端頁面中,我們自然需要一個分頁條
我們根據需要大致改造一下,增加一個首頁和末頁,同時增加一個頁數以及數據記錄統計文字
我們數據涉及到的問題基本就是上圖以及響應數據在表格中的回顯
① 響應的數據,自然我們需要 將後端所傳來包含 用戶信息的 list 集合進行遍歷回顯
- 即 需要接收並處理一個
List
集合
② 總記錄數,經後臺在數據庫查詢後給出值
- 即 需要一個
int totalCount
變量 (變量名自行決定)
③ 總頁碼,可以根據總記錄數以及每頁展示的條數計算出(後面具體講)
- 即 需要一個
int totalPage
變量
④ 當前頁碼,根據當前頁碼可以讓後臺知道你需要的數據是哪些
- 即 需要一個
int currentPage
變量
⑤ 每頁展示的條數,這個值可以暫時寫爲固定的,改進時,可以交給客戶端選擇,並且提供給後端
- 即 需要一個
int pageSize
變量
⑥ 每次查詢的起始位置,每次查詢時通過 LIMIT 語句進行限制,可以結合每頁顯示的條數得出
- 即 需要一個
int start
變量
(二) 後端實現
(1) 分頁對象
由於前端需要接收到後臺傳來的需要數據信息,我們可以爲上面我們簡單分析出所需要的東西,集合成一個分頁對象,方便我們的數據傳遞
//爲方便後期調用,加上泛型
public class PageBean<T> {
private int totalCount;//總記錄數
private int totalPage;//總頁數
private int currentPage;//當前頁碼
private int pageSize;//每頁顯示的條數
private List<T> list;//每頁顯示的數據集合
//省略對應構造,get set方法
}
(2) Servlet 代碼
- 首先需要獲取到前端傳來的:
currentPage、pageSize
兩個 String 類型的值 - 如果前端不傳遞,默認設置 這兩個變量的值,若傳遞值合理,則將其類型轉爲 int 型(前期可以先忽略這個,或者在前端設置校驗)
- 調用 service 查詢 PageBean 分頁對象,並接收其返回值
- 將PageBean對象序列化爲 json 格式,返回
@WebServlet("/route/*")
public class RouteServlet extends BaseServlet {
/**
* 分頁查詢方法
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
public void routeQuery(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String currentPageStr = request.getParameter("currentPage"); //當前頁碼
String pageSizeStr = request.getParameter("pageSize"); // 每頁顯示的條數
//當前頁碼,如果不傳遞,默認爲第1頁
int currentPage = 0;
if (currentPageStr != null && currentPageStr.length() > 0) {
currentPage = Integer.parseInt(currentPageStr);
} else {
currentPage = 1;
}
//每頁顯示條數,如果不傳遞,默認顯示8條記錄
int pageSize = 0;
if (pageSizeStr != null && pageSizeStr.length() > 0) {
pageSize = Integer.parseInt(pageSizeStr);
} else {
pageSize = 8;
}
//獲取條件查詢參數
Map<String, String[]> condition = request.getParameterMap();
//調用service查詢PageBean對象
RouteService service = new RoutrServiceImpl();
PageBean<User> userPageBean = service.pageQuery(currentPage, pageSize);
//將PageBean對象序列化爲json,返回
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json;charset=utf-8");
mapper.writeValue(response.getOutputStream(), userPageBean);
}
}
說明:以上代碼我抽取了Servlet,方便日後擴展方法,剛接觸的朋友 直接創建一個 普通的 Servlet 直接在其中編寫也是一樣可以的,熟悉的朋友,請忽略我這句話
我們需要導入 jackson spring mysql druid 的相關jar包
(3) Service 代碼
currentPage 和 pageSize 這兩個值已經確定了,我們還需要確定的有:
總記錄數 totalCount
和 總頁碼數 totalPage
以及需要回顯到前端頁面的 List
集合
總記錄數我們直接通過dao層查詢就可以了
總頁碼數我們可以通過 (總記錄數 / 每頁顯示的條數) 確定,要注意不能整除需要多出一頁
-
查詢 需要在前端頁面展示的數據 list 我們需要在SQL查詢中 使用 LIMIT進行限制,所以我們需要提供查詢 的開始點 以及每次 查多少條,這樣才能準確的找到這一頁 應該是哪些數據被回顯到頁面中,簡單的舉舉例就能得每一頁應該從哪裏開始查
即:
int start = (currentPage - 1) * pageSize
public class RouteServiceImpl implements RouteService {
private RouteDao routeDao = new RouteDaoImpl();
/**
* 分頁查詢
*
* @param currentPage
* @param pageSize
* @param condition
* @return
*/
@Override
public PageBean<User> pageQuery(int currentPage, int pageSize, Map<String, String[]> condition) {
//創建pageBean對象
PageBean<User> pageBean = new PageBean<User>();
//設置參數
pageBean.setCurrentPage(currentPage);
pageBean.setPageSize(pageSize);
//調用dao查詢總記錄數
int totalCount = routeDao.findTotalCount(condition);
pageBean.setTotalCount(totalCount);
//調用dao查詢List集合
int start = (currentPage - 1) * pageSize;
List<User> list = routeDao.findByPage(start, pageSize, condition);
pageBean.setList(list);
//計算總頁碼
int totalPage = (totalCount % pageSize) == 0 ? totalCount / pageSize : (totalCount / pageSize + 1);
pageBean.setTotalPage(totalPage);
return pageBean;
}
}
(4) Dao 代碼
/**
* 根據 start pageSize 查詢當前頁的數據集合
*
* @param start
* @param pageSize
* @return
*/
@Override
public List<User> findByPage(int start, int pageSize) {
String sql = "SELECT * FROM user_info LIMIT ? , ?";
return template.query(sql,
new BeanPropertyRowMapper<User>(User.class), start, pageSize);
}
(三) 前端實現
文檔載入完畢
$(function () {
//暫時的傳遞兩個固定值
var currentPage = 1;
var pageSize = 8;
//在這裏調用具體的功能方法
load(currentPage,pageSize);
});
function load(currentPage, pageSize){
//具體的回顯代碼,下面詳細解釋這裏些什麼
}
注意:以下代碼均寫在 load方法中
(1) ajax 異步提交
$.get("route/routeQuery", {currentPage:currentPage,pageSize:pageSize}, function (data){
//傳遞currentPage、pageSize到後端,同時回調函數返回一個data
//下面是具體代碼
})
我們下面按照這個流程順序來進行說明
(2) 數據記錄數以及總頁碼數統計
這一步,只要後臺的代碼寫好了,基本不會出現太大的問題的
$("#pageCount").html("共" + data.totalCount + "條記錄,共" + data.totalPage + "頁");
(3) 用戶信息回顯
在HTML 中我們使用了 代碼拼接的方式實現了這種需求,這個時候返回的 list集合中的一個User的數據被遍歷顯示到我們的表格中
('#userInfo_table tr:gt(0)').remove();
var s = '';
for (var i = 0; i < data.list.length; i++) {
s += '<tr><td>' + '<input type="checkbox" name="checkItem"/>' + '</td><td>' + data.list[i].uid + '</td><td>' + data.list[i].username + '</td><td>' + data.list[i].nickname + '</td><td>' + data.list[i].email + '</td><td>' + data.list[i].telephone + '</td><td>'+ data.list[i].gender + '</td><td>' + data.list[i].birthday + '</td><td>'+
'<button type="submit" class="btn btn-primary" id="updateBtn"> 修改</button>' + ' ' +
'<button type="submit" class="btn btn-primary" id="deleteBtn" ">刪除</button>' + '</td>';
}
$('#userInfo_table').append(s);
這樣我們的數據回顯就體現出來了,第一頁,正好8個數據 (Ps:前面測試時刪過一些所以編號非0開始)
(4) 首尾頁和翻頁實現
var lis = " ";
//點擊首頁代碼
var firstPage = '<li><a href="javascript:load(1,8)">首頁</a></li>';
//計算上一頁的頁碼
var previousNum = data.currentPage - 1;
if(previousNum <= 0){
previousNum = 1;
}
//上一頁翻頁的具體代碼
var previousPage = '<li class="threeword"><a href="javascript:load('+previousNum+',8)">«</a></li>';
lis += firstPage;
lis += previousPage;尾頁以及下一頁和 首頁和上一頁 基本是差不多的
(5) 頁碼的處理
如何處理頁碼比前面幾點就要複雜一點了,我們既需要用戶點擊後可以顯示出 正確的用戶信息,其次我們又需要考慮如何保證只顯示我們需要的頁碼左右的幾個頁碼,總不能有多少頁就顯示多少個頁碼
我們還是需要作出規範,就像這樣:
/*
一共展示8個頁碼,前4後3
如果前面不夠4個,後面補齊8個
如果後面不足8個,前面補齊8個
*/
var start;
var end;
//總頁碼超過8頁
if (data.totalPage < 8) {
start = 1;
end = data.totalPage;
}else{
//總頁碼超過8頁
start = data.currentPage - 4;
end = data.currentPage + 3;
//如果前面不夠4個
if (start < 1) {
start = 1;
end = start + 7;
}
//如果後面不足3個,前面補齊8個
if (end > data.totalPage ) {
end = data.totalPage;
start = end - 7;
}
}
for (var i = start; i <= end; i++) {
if (data.currentPage == i){
var li = '<li class="active"><a href="javascript:load('+i+',8)">'+ i + '</a></li>';
}else{
var li = '<li><a href="javascript:load('+i+',8)">' + i + '</a> </li>';
}
lis += li;
}
說明:上文使用了這種形式執行href="javascript:load('+i+',8)
,大家可以使用onclick自行優化一下
效果展示
首頁
末頁
總結
這篇文章到這裏就基本結束了,這個樣式是我參考某馬中的一個樣式布的局,使用 HTML + Ajax 替代了 JSP 然後後端的代碼也對應全改寫了 ,不過可以說是最簡單的一種分頁了,比較適合在JavaWeb階段 剛剛接觸分頁的朋友們瞭解一下,多多少少希望能帶給大家一些幫助,不喜勿噴
同樣,在這裏祝大家新年快樂,也希望大家都能健康平安!
結尾
如果文章中有什麼不足,或者錯誤的地方,歡迎大家留言分享想法,感謝朋友們的支持!
如果能幫到你的話,那就來關注我吧!如果您更喜歡微信文章的閱讀方式,可以關注我的公衆號
在這裏的我們素不相識,卻都在爲了自己的夢而努力 ❤
一個堅持推送原創開發技術文章的公衆號:理想二旬不止