分页查询

实现思路

  • 首先访问index.jsp页面
  • index.jsp页面发送出查询员工列表请求
  • EmployeeController来接受请求,查出员工数据
  • 来到list.jsp页面进行展示
  • 规定查询请求`URL=”/emps”
  • Restful风格URI:
    • /emp/{id} GET查询员工
    • /emp 发POST即保存员工
    • /emp/{id} PUT请求修改员工
    • /epm/{id} DELETE删除员工

业务层

  • Service业务层要调用Dao及Mapper层的方法
  • 创建EmployeeService业务层
1
2
3
4
5
6
7
8
9
10
11
12
@Service //业务逻辑组件
public class EmployeeService {
@Autowired //自动装配Mapper/Dao层
EmployeeMapper employeeMapper;

//由于Mybatis逆向工程已经生成了查询所有的方法
//查询所有员工
public List<Employee> getAll() {
// TODO Auto-generated method stub
return employeeMapper.selectByExampleWithDept(null);
}
}

分页插件

  • 首先引入PageHelper分页插件的依赖
1
2
3
4
5
6
<!--引入pageHelper分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>
  • 然后在mybatis-config.xml中注册
1
2
3
4
5
6
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>

forEach方式

控制器

  • 创建EmployeeController控制器,控制器要掉Service业务层方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Controller
public class EmployeeController {
@Autowired //自动装配Service层
EmployeeService employeeService;

// 查询员工数据(分页查询),pn为PageNum第几页,defaultValue为默认值
// @RequestMapping("/emps")
public String getEmps(
@RequestParam(value = "pn", defaultValue = "1") Integer pn,
Model model) {
// 这不是一个分页查询;
// 引入PageHelper分页插件
// 在查询之前只需要调用,传入页码,以及每页的大小
PageHelper.startPage(pn, 5);
// startPage后面紧跟的这个查询就是一个分页查询
List<Employee> emps = employeeService.getAll();
// 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。
// 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数
PageInfo page = new PageInfo(emps, 5);
model.addAttribute("pageInfo", page); //作为属性对象发送给list视图,视图通过${}取得数据

return "list"; //返回list.jsp
}
}
  • 在WEB-INF下创建views文件夹存放视图,创建list.jsp显示员工信息的视图
  • 默认只能访问index.jsp页面,所以要在index.jsp页面添加请求转发语句到emps请求,通过控制器来转到list.jsp视图·
1
<jsp:forward page="/emps"><jsp:forward>

分页请求测试

  • 新间一个MvcTest测试类,使用Spring测试模块提供的模拟测试请求功能,测试CRUD请求的正确性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//Spring4测试的时候,需要servlet3.0的支持
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath:applicationContext.xml",
"file:src/main/webapp/WEB-INF/dispatcherServlet-servlet.xml" })
public class MvcTest {
// 传入Springmvc的ioc
@Autowired
WebApplicationContext context;
// 虚拟mvc请求,获取到处理结果。
MockMvc mockMvc;

@Before //每次都要用,需要进行初始化
public void initMokcMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}

@Test
public void testPage() throws Exception {
//模拟请求拿到返回值
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "5"))
.andReturn();

//请求成功以后,请求域中会有pageInfo;我们可以取出pageInfo进行验证
MockHttpServletRequest request = result.getRequest();
PageInfo pi = (PageInfo) request.getAttribute("pageInfo");
System.out.println("当前页码:"+pi.getPageNum());
System.out.println("总页码:"+pi.getPages());
System.out.println("总记录数:"+pi.getTotal());
System.out.println("在页面需要连续显示的页码");
int[] nums = pi.getNavigatepageNums();
for (int i : nums) {
System.out.print(" "+i);
}

//获取员工数据
List<Employee> list = pi.getList();
for (Employee employee : list) {
System.out.println("ID:"+employee.getEmpId()+"==>Name:"+employee.getEmpName());
}
}
}

测试拿到数据即可

搭建分页视图

  • 使用Bootstrap搭建list.jsp分页显示页面
  • JSP页面头信息
1
2
3
4
5
6
<%@ page isELIgnored="false" %>

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  • web路径中不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题。
  • /开始的路径,找资源,以服务器的路径为标准(http://localhost:8080);需要加上项目名
1
2
3
4
<% pageContext.setAttribute("APP_PATH", request.getContextPath());%>
<script type="text/javascript" src="${APP_PATH }/static/js/jquery-1.12.4.min.js"></script>
<link href="${APP_PATH }/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
<script src="${APP_PATH }/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
  • 采用Bootstrap栅格系统搭建视图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<!-- 搭建显示页面 -->
<div class="container">
<!-- 标题 -->
<div class="row">
<div class="col-md-12">
<h1>SSM-CRUD BY PL</h1>
</div>
</div>
<!-- 按钮 -->
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary">新增</button>
<button class="btn btn-danger">删除</button>
</div>
</div>
<!-- 显示表格数据 -->
<div class="row">
<div class="col-md-12">
<table class="table table-hover">
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
<c:forEach items="${pageInfo.list }" var="emp">
<tr>
<th>${emp.empId }</th>
<th>${emp.empName }</th>
<th>${emp.gender=="M"?"男":"女" }</th>
<th>${emp.email }</th>
<!--部门信息-->
<th>${emp.department.deptName }</th>
<th>
<button class="btn btn-primary btn-sm">
<!--字体图标-->
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
编辑
</button>
<button class="btn btn-danger btn-sm">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
删除
</button>
</th>
</tr>
</c:forEach>
</table>
</div>
</div>

<!-- 显示分页信息 -->
<div class="row">
<!--分页文字信息 -->
<div class="col-md-6">当前 ${pageInfo.pageNum }页,总${pageInfo.pages }
页,总 ${pageInfo.total } 条记录</div>
<!-- 分页条信息 -->
<div class="col-md-6">
<nav aria-label="Page navigation">
<ul class="pagination">
<li><a href="${APP_PATH }/emps?pn=1">首页</a></li>
<!--如果有上一页就发请求显示,否则则首页按钮和前进按钮禁用-->
<c:if test="${pageInfo.hasPreviousPage }">
<li><a href="${APP_PATH }/emps?pn=${pageInfo.pageNum-1}"
aria-label="Previous"> <span aria-hidden="true">&laquo;</span>
</a></li>
</c:if>

<!--拿到页码数-->
<c:forEach items="${pageInfo.navigatepageNums }" var="page_Num">
<!--判断当前页码是否是当前页,如果是则高亮显示-->
<c:if test="${page_Num == pageInfo.pageNum }">
<!--发送请求-->
<li class="active"><a href="#">${page_Num }</a></li>
</c:if>
<!--判断当前页码不是当前页,则取消高亮显示-->
<c:if test="${page_Num != pageInfo.pageNum }">
<li><a href="${APP_PATH }/emps?pn=${page_Num }">${page_Num }</a></li>
</c:if>

</c:forEach>

<!--判断是否有下一页,有才显示,否则末尾按钮禁用-->
<c:if test="${pageInfo.hasNextPage }">
<li><a href="${APP_PATH }/emps?pn=${pageInfo.pageNum+1 }"
aria-label="Next"> <span aria-hidden="true">&raquo;</span>
</a></li>
</c:if>
<!--pages表示总页数-->
<li><a href="${APP_PATH }/emps?pn=${pageInfo.pages}">末页</a></li>
</ul>
</nav>
</div>
</div>

</div>

分页的参数和属性可以查看官方文档,可以合理运用三元运算符${emp.gender=="M"?"男":"女" }

JSON数据方式

  • 以上只适用于浏览器客户端,对于安卓或者IOS等移动客户端是行不通的。所以可以通过返回JSON数据进行统一解析
  • 使用纯Ajax进行查询,index.jsp页面直接发送ajax请求进行员工分页数据的查询
  • 服务器将查出的数据,以JSON字符串的形式返回给浏览器
  • 浏览器收到JS字符串,可以使用JS对JSON进行解析,使用JS通过DOM增删改改变页面
  • 返回JSON。实现客户端的无关性

通用类

  • 在实体包bean下创建通用的返回JSON的类Msg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class Msg {
//状态码 100-成功 200-失败
private int code;
//提示信息
private String msg;

//用户要返回给浏览器的数据
private Map<String, Object> extend = new HashMap<String, Object>();

public static Msg success(){
Msg result = new Msg();
result.setCode(100);
result.setMsg("处理成功!");
return result;
}

public static Msg fail(){
Msg result = new Msg();
result.setCode(200);
result.setMsg("处理失败!");
return result;
}
//实现链式调用
public Msg add(String key,Object value){
this.getExtend().put(key, value);
return this;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public Map<String, Object> getExtend() {
return extend;
}

public void setExtend(Map<String, Object> extend) {
this.extend = extend;
}

}

控制器

  • 修改EmployeeeController控制器,添加方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//导入jackson包。此包负责将pageInfo对象转为JSON字符串
@RequestMapping("/emps")
@ResponseBody
public Msg getEmpsWithJson(
@RequestParam(value = "pn", defaultValue = "1") Integer pn) {
// 引入PageHelper分页插件
// 在查询之前只需要调用,传入页码,以及每页的大小
PageHelper.startPage(pn, 5);
// startPage后面紧跟的这个查询就是一个分页查询
List<Employee> emps = employeeService.getAll();
// 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。
// 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数
PageInfo page = new PageInfo(emps, 5);
return Msg.success().add("pageInfo", page);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
{
"code": 100,
"msg": "处理成功!",
"extend": {
"pageInfo": {
"pageNum": 1,
"pageSize": 5,
"size": 5,
"startRow": 1,
"endRow": 5,
"total": 201,
"pages": 41,
"list": [{
"empId": 1,
"empName": "jerry",
"gender": "M",
"email": "jerry@pl.com",
"dId": 1,
"department": {
"deptId": 1,
"deptName": "开发部"
}
}, {
"empId": 2,
"empName": "5f0a10",
"gender": "M",
"email": "5f0a10@pl.com",
"dId": 1,
"department": {
"deptId": 1,
"deptName": "开发部"
}
}, {
"empId": 3,
"empName": "5c3ea1",
"gender": "M",
"email": "5c3ea1@pl.com",
"dId": 1,
"department": {
"deptId": 1,
"deptName": "开发部"
}
}, {
"empId": 4,
"empName": "1c8592",
"gender": "M",
"email": "1c8592@pl.com",
"dId": 1,
"department": {
"deptId": 1,
"deptName": "开发部"
}
}, {
"empId": 5,
"empName": "65b5b3",
"gender": "M",
"email": "65b5b3@pl.com",
"dId": 1,
"department": {
"deptId": 1,
"deptName": "开发部"
}
}],
"prePage": 0,
"nextPage": 2,
"isFirstPage": true,
"isLastPage": false,
"hasPreviousPage": false,
"hasNextPage": true,
"navigatePages": 5,
"navigatepageNums": [1, 2, 3, 4, 5],
"navigateFirstPage": 1,
"navigateLastPage": 5,
"lastPage": 5,
"firstPage": 1
}
}
}

视图修改

  • 修改index.jsp页面,一进入首页就发送Ajax拿取数据并解析
  • 同样要引入Boostrap框架,搭建的显示页面如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!-- 搭建显示页面 -->
<div class="container">
<!-- 标题 -->
<div class="row">
<div class="col-md-12">
<h1>SSM-CRUD BY PL</h1>
</div>
</div>
<!-- 按钮 -->
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary" id="emp_add_modal_btn">新增</button>
<button class="btn btn-danger" id="emp_delete_all_btn">删除</button>
</div>
</div>
<!-- 显示表格数据 -->
<div class="row">
<div class="col-md-12">
<table class="table table-hover" id="emps_table">
<thead>
<tr>
<th>
<input type="checkbox" id="check_all"/>
</th>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!--发送Ajax构建表格体-->
</tbody>
</table>
</div>
</div>

<!-- 显示分页信息 -->
<div class="row">
<!--分页文字信息 -->
<div class="col-md-6" id="page_info_area"></div>
<!-- 分页条信息 -->
<div class="col-md-6" id="page_nav_area"></div>
</div>

</div>

发送Ajax请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var totalRecord,currentPage; //全局变量,总记录数和当前页
//1、页面加载完成以后,直接去发送ajax请求,要到分页数据
$(function(){
//去首页
to_page(1);
});

function to_page(pn){
$.ajax({
url:"${APP_PATH}/emps",
data:"pn="+pn,
type:"GET",
success:function(result){
//console.log(result);
//1、解析并显示员工数据
build_emps_table(result);
//2、解析并显示分页信息
build_page_info(result);
//3、解析显示分页条数据
build_page_nav(result);
}
});
}
  • 解析并显示员工数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function build_emps_table(result){
//在传入新数据之前,需要清空table表格
$("#emps_table tbody").empty();
//对照JSON进行解析
var emps = result.extend.pageInfo.list;
//遍历
$.each(emps,function(index,item){
var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>");
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName);
var genderTd = $("<td></td>").append(item.gender=='M'?"男":"女");
var emailTd = $("<td></td>").append(item.email);
var deptNameTd = $("<td></td>").append(item.department.deptName);
/**
<button class="">
<span class="" aria-hidden="true"></span>
编辑
</button>
*/
//addClass添加类名 append在元素内部添加DOM元素
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
//为编辑按钮添加一个自定义的属性,来表示当前员工id
editBtn.attr("edit-id",item.empId);
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
//为删除按钮添加一个自定义的属性来表示当前删除的员工id
delBtn.attr("del-id",item.empId);
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
//var delBtn =
//append方法执行完成以后还是返回原来的元素<tr></tr>
$("<tr></tr>").append(checkBoxTd)
.append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody"); //添加到表格体中
});
}
  • 解析并显示分页信息
1
2
3
4
5
6
7
8
9
//解析左下角显示分页信息
function build_page_info(result){
$("#page_info_area").empty();
$("#page_info_area").append("当前"+result.extend.pageInfo.pageNum+"页,总"+
result.extend.pageInfo.pages+"页,总"+
result.extend.pageInfo.total+"条记录");
totalRecord = result.extend.pageInfo.total;
currentPage = result.extend.pageInfo.pageNum;
}
  • 解析显示分页条数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//解析显示分页条,点击分页要能去下一页....
function build_page_nav(result){
//page_nav_area
$("#page_nav_area").empty();
var ul = $("<ul></ul>").addClass("pagination");

//构建元素
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href","#"));
//转义字符&laquo;表示<前一页
var prePageLi = $("<li></li>").append($("<a></a>").append("&laquo;"));
//如果当前页没有前一页了,就禁掉首页和前一页按钮
if(result.extend.pageInfo.hasPreviousPage == false){
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
}else{
//未禁用才绑定事件发请求
//为元素添加点击翻页的事件
firstPageLi.click(function(){
to_page(1); //首页
});
prePageLi.click(function(){
//传入当前页页码并调用函数
to_page(result.extend.pageInfo.pageNum -1);
});
}

//转义字符&raquo;表示>下一页
var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href","#"));

//如果当前页没有后一页了,就禁用末页和后一页按钮
if(result.extend.pageInfo.hasNextPage == false){
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
}else{
//未禁用才绑定事件发请求
nextPageLi.click(function(){
//传入页码并调用函数
to_page(result.extend.pageInfo.pageNum +1);
});
lastPageLi.click(function(){
//传入总页数,直接跳到最后一页
to_page(result.extend.pageInfo.pages);
});
}

//添加首页和前一页 的提示
ul.append(firstPageLi).append(prePageLi);
//1,2,3遍历给ul中添加页码提示
$.each(result.extend.pageInfo.navigatepageNums,function(index,item){

var numLi = $("<li></li>").append($("<a></a>").append(item));
if(result.extend.pageInfo.pageNum == item){
numLi.addClass("active"); //当前页高亮显示
}
numLi.click(function(){
to_page(item);
});
ul.append(numLi);
});
//添加下一页和末页 的提示
ul.append(nextPageLi).append(lastPageLi);

//把ul加入到nav
var navEle = $("<nav></nav>").append(ul);
navEle.appendTo("#page_nav_area");
}

PageHelper分页记得配置参数合理化参数