综合案例
目标:
- 实现查询所有品牌功能
- 实现添加品牌功能
- 理解
BaseServlet
思想- 实现批量删除功能
- 实现分页查询功能
- 实现条件查询功能
# 功能介绍
以上是我们要在综合案例课程中实现的功能,除了对品牌数据的增删改查功能外,还有一些复杂的功能,比如:批量删除、分页查询、条件查询等
- 批量删除:每条数据前设置复选框,当选中多条数据并点击批量删除按钮后,会发送请求到后端服务并删除数据库中指定的多条品牌数据;
- 分页查询:当数据库中有很多数据时,我们不可能将所有的数据展示在一页里,这个时候就需要分页展示数据;
- 条件查询:数据量大时,需要精确的查询来过滤想看到的数据,这个时候就需要通过条件查询。
注意
修改品牌和删除品牌功能在课堂上不做讲解,留给同学们课下练习。
# 环境准备
- 将资料的
brand-case
模块导入到Idea中 - 创建数据库并执行资料中的
tb_brand.sql
脚本
# 工程准备
将下载好的代码压缩包brand-case.zip
解压到某一非中文无特殊字符的目录下面,然后通过Idea的模块导入功能完成代码导入:
# 数据库准备
- 创建数据库
- 创建表
在brand_case数据库下执行tb_brand.sql
脚本
-- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand
(
-- id 主键
id int primary key auto_increment,
-- 品牌名称
brand_name varchar(20),
-- 企业名称
company_name varchar(20),
-- 排序字段
ordered int,
-- 描述信息
description varchar(100),
-- 状态:0:禁用 1:启用
status int
);
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status)
values
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1)
;
SELECT * FROM tb_brand;
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
# 查询所有功能
如上图所示是查询所有品牌数据在页面上的展示效果,要开发这样的功能,需要先分析清楚几个问题:
什么时候发送异步请求?页面加载完毕后就需要在页面上看到所有的品牌数据,所以在
mounted()
这个构造函数中发送查询所有品牌数据的异步 请求;请求需要携带参数吗?不需要携带参数
响应的数据格式是什么?HTML?JSON?...
1
整体流程如下:
接下来我们采用和之前学习JSP技术时相反的顺序开发前后端程序,也即是先开发后端程序,再开发前端程序。
# 查询所有功能后端实现
# 查询所有功能dao
方法实现
在com.itheima.mapper.BrandMapper
接口中定义抽象方法:
package com.itheima.mapper;
import com.itheima.pojo.Brand;
import java.util.List;
public interface BrandMapper {
/**
* 查询所有品牌数据
* @return
*/
List<Brand> selectAll();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
接下来通过MyBatisX插件(使用Alt+Enter组合键)生成响应的Statement
生成statement
后,我们在BrandMapper.xml
中就可以看到内容了:
但是由于tb_brand
表中有些字段名和实体类中的属性名没有完全对应,比如品牌名,企业名称。因此需要在com/itheima/mapper/BrandMapper.xml
映射配置文件中使用resultMap
标签定义结果集映射,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.BrandMapper">
<resultMap id="brandResultMap" type="Brand">
<result column="brand_name" property="brandName" />
<result column="company_name" property="companyName" />
</resultMap>
<select id="selectAll" resultType="com.itheima.pojo.Brand"></select>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
定义完结果集映射关系后,在selectAll
中修改resultType
为resultMap
,引用我们上面定义的brandResultMap
映射关系,并在select
开始标签和关闭标签内书写SQL
语句,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.BrandMapper">
<resultMap id="brandResultMap" type="Brand">
<result column="brand_name" property="brandName" />
<result column="company_name" property="companyName" />
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select
id,
brand_name,
company_name,
ordered,
description,
status
from
tb_brand
</select>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 查询所有功能service
方法实现
在com.itheima.service
包下的BrandService
接口中定义查询所有品牌数据抽象方法:
package com.itheima.service;
import com.itheima.pojo.Brand;
import java.util.List;
public interface BrandService {
/**
* 查询所有品牌数据
* @return
*/
List<Brand> selectAll();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在com.itheima.service.impl
包下的BrandServiceImpl
类中重写在BrandService
接口中定义的抽象方法,impl
一般用来存放service
层接口的实现类。
思考:此处为什么要给`service`定义接口
原因是service
层定义了接口后,在servlet
中可以使用多态的形式创建Service
实现类对象,如下:
@WebServlet("/selectAllServlet")
public class SelectAllServlet extends HttpServlet {
private BrandService brandService = new BrandServiceImpl();
}
2
3
4
这里使用多态是方便后期我们解除Servlet
和Service
之间的耦合,从上面的代码可以看到SelectAllServlet
类和BrandServiceImpl
类之间是耦合在一起的,如果后期BrandService
接口有了更好的实现类,那就需要修改SelectAllServlet
类中的代码。以后我们学习了Spring
框架后就可以解除SelectAllServlet
类和红色框括起来的代码耦合,但是我们现在还做不到,当前我们只需要理解为什么在这里定义接口即可。
package com.itheima.service.impl;
import com.itheima.mapper.BrandMapper;
import com.itheima.pojo.Brand;
import com.itheima.service.BrandService;
import com.itheima.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
public class BrandServiceImpl implements BrandService {
// 创建SqlSessionFactory 工厂对象
SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
@Override
public List<Brand> selectAll() {
// 1. 获取SqlSession对象
SqlSession sqlSession = factory.openSession(true);
// 2. 获取BrandMapper
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
// 3. 调用方法
List<Brand> brands = mapper.selectAll();
// 4. 释放资源
sqlSession.close();
// 5. 返回结果
return brands;
}
}
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
# 查询所有功能servlet
实现
在com.itheima.web.servlet
包下定义名为SelectAllServlet
的servlet
,用来实现查询所有品牌数据,该servlet
逻辑如下:
- 调用
service
的selectAll()
方法查询所有的品牌数据,并返回结果 - 将返回的结果转换为
json
数据 - 响应
json
数据
代码如下:
package com.itheima.web.servlet;
import com.alibaba.fastjson.JSON;
import com.itheima.pojo.Brand;
import com.itheima.service.BrandService;
import com.itheima.service.impl.BrandServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet("SelectAllServlet")
public class SelectAllServlet extends HttpServlet {
private BrandService brandService = new BrandServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 调用service查询
List<Brand> brands = brandService.selectAll();
// 2. 转化为json
String json = JSON.toJSONString(brands);
// 3. 写数据
// 告知浏览器响应的数据格式以及字符集编码
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
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
# 测试后端程序
配置Tomcat服务器并部署brand-case应用:
配置完成后,启动Tomcat服务器,在浏览器地址栏输入http://localhost/brand-case/selectAllServlet
# 查询所有功能前端实现
前端需要在页面加载完毕后发送Ajax请求,所以发送请求的逻辑应该放在mounted()
钩子函数中,而响应回来的数据需要赋值给表格绑定的数据
模型,从下图可以看出表格绑定的数据模型是tableData
前端代码如下:
// 创建Vue核心对象
new Vue({
el: "#app",
data() {
return {
tableData: [],
};
},
mounted() {
// 当页面加载完成后,发送异步请求,获取数据
this.selectAll();
},
methods: {
// 查询所有品牌数据
selectAll() {
var _this = this;
axios({
method: "get",
url: "http://localhost/brand-case/selectAllServlet",
}).then(function (resp) {
_this.tableData = resp.data;
});
},
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 添加功能
上图是添加数据的对话框,当点击提交按钮后就需要将数据提交到后端,并将数据保存到数据库中,下图是整体的流程:
页面发送请求时,需要将输入框输入的内容提交给后端程序,而这里的参数是以json
格式进行传递的,具体的数据格式如下:
{
"status": 1,
"brandName": "鸿星尔克",
"companyName": "鸿星尔克",
"id": "",
"ordered": 200,
"description": "to be no.1"
}
2
3
4
5
6
7
8
注意
由于是添加数据,所以上述json
数据中id
是没有值的。
# 添加功能后端实现
# 添加功能dao
方法实现
在BrandMapper
接口中定义add(Brand brand)
添加方法,并在BrandMapper.xml
映射配置文件中书写SQL
语句
/**
* 添加品牌
* @param brand
*/
int add(Brand brand);
2
3
4
5
<insert id="add" parameterType="Brand" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand(
brand_name,
company_name,
ordered,
description,
status
)
values(
#{brandName},
#{companyName},
#{ordered},
#{description},
#{status}
)
</insert>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 添加功能service
方法实现
在BrandService
接口中定义boolean add(Brand brand)
添加品牌数据的业务逻辑方法
/**
* 添加品牌
* @param brand
* @return
*/
boolean add(Brand brand);
2
3
4
5
6
在BrandServiceImpl
类中重写add()
方法,并进行业务逻辑实现
@Override
public boolean add(Brand brand) {
// 1. 获取SqlSession对象
SqlSession sqlSession = factory.openSession(true);
// 2. 获取BrandMapper
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
// 3. 调用方法
int flag = mapper.add(brand);
// 4. 释放资源
sqlSession.close();
// 5. 返回结果
return flag > 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
# 添加功能servlet
方法实现
在com.itheima.web.servlet
包写定义名为AddServlet
的Servlet,该Servlet
的逻辑如下:
- 接收页面提交的数据,页面提交的数据是JSON格式的数据,所以此处需要使用输入流读取数据;
- 将接收到的数据转换为
Brand
对象 - 调用
service
的add()
方法进行添加的业务逻辑处理 - 给浏览器添加响应成功的标识,给浏览器响应
success
字符串标识成功
servlet
代码如下:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 接收品牌数据
BufferedReader br = request.getReader();
String line = null;
StringBuffer sb = new StringBuffer();
while ((line = br.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
// 2. 转为Brand对象
Brand brand = JSON.parseObject(json, Brand.class);
// 3. 调用service中的添加方法
boolean flag = brandService.add(brand);
// 4. 返回响应成功或失败的标识
if (flag) {
response.getWriter().write("success");
} else {
response.getWriter().write("failure");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 添加功能前端实现
上图左边是页面效果,里面的提交按钮可以通过上图右边看出绑定了一个单击事件,该时间绑定的是addBrand
函数,所以添加数据功能的逻辑
代码应该写在addBrand()
方法中,在此方法中需要发送异步请求并将表单中输入的数据作为参数进行传递。
- 在页面上增加新增按钮
<!--按钮-->
<el-row>
<el-button type="primary" plain @click="showDialog('add')">新增</el-button>
</el-row>
2
3
4
- 在页面上增加对话框
<!--添加数据对话框表单-->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="30%">
<el-form ref="form" :model="brand" label-width="80px">
<el-form-item label="品牌名称">
<el-input v-model="brand.brandName"></el-input>
</el-form-item>
<el-form-item label="企业名称">
<el-input v-model="brand.companyName"></el-input>
</el-form-item>
<el-form-item label="排序">
<el-input v-model="brand.ordered"></el-input>
</el-form-item>
<el-form-item label="品牌描述">
<el-input type="textarea" v-model="brand.description"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-switch
v-model="brand.status"
active-value="1"
inactive-value="0"
></el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addBrand">提交</el-button>
<el-button @click="closeDialog()">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
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
- 定义数据模型和绑定监听事件
// 创建Vue核心对象
new Vue({
el: "#app",
data() {
return {
tableData: [],
dialogVisible: false,
dialogTitle: "",
brand: {
status: 0,
brandName: "",
companyName: "",
id: null,
ordered: null,
description: "",
},
};
},
mounted() {
// 当页面加载完成后,发送异步请求,获取数据
this.selectAll();
},
methods: {
// 查询所有品牌数据
selectAll() {
var _this = this;
axios({
method: "get",
url: "http://localhost/brand-case/selectAllServlet",
}).then(function (resp) {
_this.tableData = resp.data;
});
},
// 展示对话框
showDialog(param) {
let _this = this;
_this.dialogVisible = true;
// 清空表单数据
if ("add" == param) {
_this.dialogTitle = "添加品牌";
}
if ("update" == param) {
_this.dialogTitle = "修改品牌";
}
_this.$refs["form"].resetFields();
},
// 关闭对话框
closeDialog() {
let _this = this;
_this.dialogVisible = false;
},
// 添加品牌
addBrand() {
let _this = this;
// 发送ajax请求,添加数据
axios({
method: "post",
url: "http://localhost/brand-case/addServlet",
data: _this.brand,
}).then(function (resp) {
if (resp.data == "success") {
// 添加成功关闭窗口
_this.dialogVisible = false;
// 重新查询数据
_this.selectAll();
// 弹出成功消息提示
_this.$message({
message: "添加品牌成功",
type: "success",
});
} else {
_this.$message({
message: "添加品牌失败",
type: "danger",
});
}
});
},
},
});
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
# servlet
优化
# 问题导入
通过之前的两个功能,我们发现没实现一个功能都需要定义一个servlet
,一个模块需要实现增删改查功能,就需要 4 个servlet
,模块多时就会
造成servlet
泛滥,此时我们就希望servlet
像service
一样,一个模块定义一个servlet
,而每一个功能只需要在该servlet
中定义对应
的方法,例如下面的代码:
@WebServlet("/brand/*")
public class BrandServlet {
// 查询所有
public void selectAll() {}
// 添加
public void add() {}
// 修改
public void update() {}
// 删除
public void delete() {}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
而我们知道发送请求serlvet
,tomcat
会自动的调用service()
方法,之前我们在自定义的servlet
中重写doGet()
和doPost()
方法,
当我们访问该servlet
时会根据请求方式将请求分发给doGet()
或者doPost()
方法,如图:
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
doGet(); // 说明是GET请求,调用doGet()方法
} else if (method.equals(METHOD_POST)) {
doPost(); // 说明是post请求,调用doPost()方法
}
}
2
3
4
5
6
7
8
那么我们也可以仿照这样的请求分发思想,在service()
方法中根据具体的操作调用对应的方法,如:查询所有就调用selectAll()
方法,添加品牌
就调用add()
方法。
为了做到通用,我们可以定义一个通用的servlet
类,在定义其他的servlet
时不再继承HttpServlet
,而继承我们自定义的BaseServlet
,
在BaseServlet
中调用具体servlet
中(如BrandServlet
)对应的方法。
public class BaseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 进行请求分发
}
}
2
3
4
5
6
BrandServlet
定义就需要修改为:
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
// 用户实现分页查询
public void selectAll() {}
// 添加企业
public void add() {}
// 修改企业
public void update() {}
// 删除企业
public void delete() {}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
那么如何在BaseServlet
中调用对应的方法呢,比如查询所有就调用selectAll()
方法,在讲如何实现之前我们需要先补充一个JavaSE进阶相关
的知识点:反射
学习完反射,我们就可以规定在发送请求时,请求资源的二级路径(/brandServlet/selectAll
)和需要调用的方法名相同,如:
- 查询所有数据的路径以后可以写成:
http:/localhost/brand-case/brandServlet/selectAll
- 添加数据的路径以后写成:
http://localhost/brand-case/brandServlet/add
- 修改数据的路径以后写成:
http://localhost/brand-case/brandServlet/update
- 删除数据的路径以后就可以写成:
http://localhost:/brandServlet/delete
这样,在BaseServlet
中就需要获取资源的二级路径作为方法名,然后调用该方法即可
public class BaseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取请求路径
String uri = req.getRequestURI(); // 例如路径为:/brand-case/brand/selectAll
//2. 获取最后一段路径,方法名
int index = uri.lastIndexOf('/');
String methodName = uri.substring(index + 1); // 获取到资源的二级路径 selectAll
//2. 执行方法
//2.1 获取BrandServlet /UserServlet 字节码对象 Class
//System.out.println(this);
Class<? extends BaseServlet> cls = this.getClass();
//2.2 获取方法 Method对象
try {
Method method = cls.getMethod(methodName,???);
//4,调用该方法
method.invoke(this,???);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
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
通过上面代码发现根据方法名获取对应方法的Method
对象时需要指定方法参数的字节码对象,解决这个问题,可以将方法的参数类型规定死,而方法
中可能需要用到request
对象和response
对象,所以指定方法的参数为HttpServletRequest
和HttpServletResponse
,那么BrandServlet
代码可以改进为
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
//用户实现分页查询
public void selectAll(HttpServletRequest req, HttpServletResponse resp) {}
//添加企业信息
public void add(HttpServletRequest req, HttpServletResponse resp) {}
//修改企业信息
public void update(HttpServletRequest req, HttpServletResponse resp) {}
//删除企业信息
public void delete(HttpServletRequest req, HttpServletResponse resp) {}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
BaseServlet
代码可以改进为:
public class BaseServlet extends HttpServlet {
//根据请求的最后一段路径来进行方法分发
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取请求路径
String uri = req.getRequestURI(); // 例如路径为:/brand-case/brand/selectAll
//2. 获取最后一段路径,方法名
int index = uri.lastIndexOf('/');
String methodName = uri.substring(index + 1); // 获取到资源的二级路径 selectAll
//2. 执行方法
//2.1 获取BrandServlet /UserServlet 字节码对象 Class
//System.out.println(this);
Class<? extends BaseServlet> cls = this.getClass();
//2.2 获取方法 Method对象
try {
Method method = cls.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
//2.3 执行方法
method.invoke(this,req,resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
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
# 代码优化
# 后端代码优化
定义了BaseServlet
后,针对品牌模块我们定义一个BrandServlet
的Servlet
,并使其继承BaseServlet
,在BrandServlet
中定义以下
功能的方法:
- 查询所有功能:方法名声明为
selectAll
,并将之前的代码拷贝过来 - 添加数据功能:方法声明为
add
,将之前的代码拷贝过来
具体代码如下:
package com.itheima.web.servlet;
import com.alibaba.fastjson.JSON;
import com.itheima.pojo.Brand;
import com.itheima.service.BrandService;
import com.itheima.service.impl.BrandServiceImpl;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
private BrandService brandService = new BrandServiceImpl();
public void selectAll(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 1. 调用service查询
List<Brand> brands = brandService.selectAll();
// 2. 转化为json
String json = JSON.toJSONString(brands);
// 3. 写数据
// 告知浏览器响应的数据格式以及字符集编码
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
}
public void add(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 1. 接收品牌数据
BufferedReader br = request.getReader();
String line = null;
StringBuffer sb = new StringBuffer();
while ((line = br.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
// 2. 转为Brand对象
Brand brand = JSON.parseObject(json, Brand.class);
// 3. 调用service中的添加方法
boolean flag = brandService.add(brand);
// 4. 返回响应成功或失败的标识
if (flag) {
response.getWriter().write("success");
} else {
response.getWriter().write("failure");
}
}
}
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
# 前端代码优化
页面中之前发送的请求的路径都需要进行修改,selectAll()
函数中发送异步请求的url
应该修改为: http://localhost/brand-case/brand/selectAll
// 查询所有品牌数据
selectAll() {
var _this = this;
axios({
method: "get",
url: "http://localhost/brand-case/brand/selectAll"
}).then(function (resp) {
_this.tableData = resp.data;
});
},
2
3
4
5
6
7
8
9
10
// 添加品牌
addBrand() {
let _this = this;
// 发送ajax请求,添加数据
axios({
method: "post",
url: "http://localhost/brand-case/brand/add",
data: _this.brand
}).then(function(resp) {
if (resp.data == "success") {
// 添加成功关闭窗口
_this.dialogVisible = false;
// 重新查询数据
_this.selectAll();
// 弹出成功消息提示
_this.$message({
message: "添加品牌成功",
type: "success"
})
} else {
_this.$message({
message: "添加品牌失败",
type: "danger"
})
}
});
}
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
# 批量删除
如上图所示点击多条数据前的复选框就意味着要删除这些数据,而点击批量删除按钮后,需要让用户确认一下,排除用户误操作,当用户确定后就
需要给后端发送请求并携带需要删除数据的多个id
值,后端程序删除数据库中的数据,具体的流程如下:
注意
前端发送请求时需要将要删除的多个id
值以json
格式提交给后端,而该json
格式数据如下:
[1, 2, 3, 4]
# 批量删除后端实现
# 批量删除dao
方法实现
在BrandMapper
接口中定义deleteByIds()
添加方法,这里需要用到动态sql
接口方法声明如下:
/**
* 批量删除
* @param ids
* @return
*/
int deleteByIds(@Param("ids") int[] ids);
2
3
4
5
6
在BrandMapper.xml
映射配置文件中添加statement
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
2
3
4
5
6
# 批量删除service
方法实现
在BrandService
接口中定义deleteByIds()
批量删除的业务逻辑方法
/**
* 批量删除
* @param ids
* @return
*/
boolean deleteByIds(int[] ids);
2
3
4
5
6
在BrandServiceImpl
类中重写deleteByIds()
方法,并进行业务逻辑实现
@Override
public boolean deleteByIds(int[] ids) {
// 1. 获取SqlSession
SqlSession sqlSession = factory.openSession(true);
// 2. 获取BrandMapper
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
// 3. 调用方法
int count = mapper.deleteByIds(ids);
// 4. 释放资源
sqlSession.close();
// 5. 返回结果
return count > 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
# 批量删除servlet
实现
在BrandServlet
类中定义deleteByIds()
方法,而该方法的逻辑如下:
- 接收页面提交的数据。页面到时候提交的数据是
json
格式的数据,所以此处需要使用输入流读取数据 - 接收页面提交的数据。页面到时候提交的数据是
json
格式的数据,所以此处需要使用输入流读取数据 - 将接收到的数据转换为
int[]
数组 - 调用
service
的deleteByIds()
方法进行批量删除的业务逻辑处理 - 给浏览器响应添加成功的标识,这里直接给浏览器响应
success
字符串表示成功
代码如下:
public void deleteByIds(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 1. 接收数据
BufferedReader br = request.getReader();
String line = null;
StringBuffer sb = new StringBuffer();
while ((line = br.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
// 2. 转为int[]
int[] ids = JSON.parseObject(json, int[].class);
// 3. 调用service批量删除
boolean flag = brandService.deleteByIds(ids);
// 4. 响应
if (flag) {
response.getWriter().write("success");
} else {
response.getWriter().write("failure");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 批量删除前端实现
此功能的前端代码实现稍微有点复杂,分为一下几步实现:
# 添加选择框和索引号
# 批量删除获取选中的id
值
从上图可以看出表格复选框绑定了一个selection-change
事件,该时间是当选择项发生变化时会触发,该事件绑定了handleSelectionChange
函数,而该函数有一个参数val
,该参数是获取选中行的数据,如下:
我们只需要将所有选中数据的id
值提交给服务端即可,获取id
的逻辑我们放在批量删除按钮绑定的函数中,在批量删除按钮绑定的单击事件,
并给绑定触发时调用的函数,如下:
在Vue
对象中的methods
中定义deleteByIds()
函数,在该函数中从multipleSelection
数据模型中获取所选数据的id
值,要完成这个功能
需要在Vue
对象中定义一个数据模型selectedIds:[]
,在deleteByIds()
函数中遍历multipleSelection
数组,并获取每一个所选数据
的id
值存储到selectedIds
数组中,代码如下:
// 1. 创建id数组[1, 2, 3],从this.multipleSelection获取即可
for (let i = 0; i < this.multipleSelection.length; i++>) {
let selectionElement = this.multipleSelection[i];
this.selectedIds[i] = selectionElement.id;
}
2
3
4
5
# 批量删除发送异步请求
使用axios发送异步请求并经上一步获取到的存储所有的id
数组作为请求参数
//2. 发送AJAX请求
var _this = this;
// 发送ajax请求,添加数据
axios({
method: "post",
url: "http://localhost:8080/brand-case/brand/deleteByIds",
data: _this.selectedIds,
}).then(function (resp) {
if (resp.data == "success") {
//删除成功
// 重新查询数据
_this.selectAll();
// 弹出消息提示
_this.$message({
message: "恭喜你,删除成功",
type: "success",
});
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 批量删除确定框实现
由于删除操作比较危险,有时候可能出现用户误操作,所以在点击了按钮后需要先给用户确认提示,而确认框在elementUI
中也提供了,如下图
而在点击确定按钮后,需要执行之前删除的逻辑,前端代码如下:
// 批量删除
deleteByIds() {
// 弹出确认提示框
this.$confirm("此操作将删除数据,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
// 用户点击确认
// 1. 创建id数组,从this.multipleSelection获取
for (let i = 0; i < this.multipleSelection.length; i++) {
let selectionElement = this.multipleSelection[i];
this.selectedIds[i] = selectionElement.id;
}
// 2. 发送ajax请求
let _this = this;
axios({
method: "post",
url: "http://localhost/brand-case/brand/deleteByIds",
data: _this.selectedIds
}).then(function(resp) {
if (resp.data == "success") {
// 删除成功,重新查询数据
_this.selectAll();
// 弹出消息提示
_this.$message({
message: "删除成功",
type: "success"
})
} else {
_this.$message({
message: "删除失败",
type: "danger"
})
}
})
}).catch(() => {
// 用户点击取消
this.$message({
type: "info",
message: "已取消删除"
})
})
}
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
# 分页查询
我们之前做的查询所有功能中将数据库中所有的数据查询出来并展示到页面上,试想如果数据库中的数据有很多,将数据全部展示出来肯定不现实, 那么如何解决这个问题呢?几乎所有的网站都会使用分页来解决这个问题,每次只显示一页的数据,不如一页展示 10 条数据,如果还想看其他的数据, 可以通过点解页码进行查询
# 需求分析
# 分页查询SQL
分页查询也是从数据库进行查询的,所以我们要分析对应的SQL
语句应该怎么写。分页查询使用LIMIT
关键字,格式为:
LIMIT 开始索引 每页显示的条数,以后前端页面在发送请求携带参数时,它并不明确开始索引是什么,但是它知道查询第几页,所以开始索引
需要在后端进行计算,计算的公式是:开始索引 = (当前页面 - 1) * 每页显示条数
比如查询第一页的数据,SQL
语句:
select * from tb_brand limit 0, 5;
查询第二页的数据,SQL
语句:
select * from tb_brand limit 5, 5;
第三页数据,SQL
语句:
select * from tb_brand limit 10, 5;
# 前后端数据分析
分页查询功能比较复杂,需要先分析清楚下面两个问题:
前端需要传递什么参数给后端:
- 当前页面:
currentPage
- 每页显示条数:
pageSize
- 当前页面:
后端需要响应什么数据给前端:
- 当前页需要展示的数据,后端一般会存储到
List
集合中 - 总共记录数,在上图中需要展示总的记录数,所以这部分数据也需要
- 当前页需要展示的数据,后端一般会存储到
这两部分需要封装到PageBean
对象中,并将该对象转换为json
格式的数据响应给浏览器
通过上面的分析我们需要先在pojo
包下创建PageBean
类,为了做到通用,将其定义成泛型类,代码如下:
package com.itheima.pojo;
import java.util.List;
/**
* 分页查询JavaBean
*/
public class PageBean<T> {
// 总记录数
private int totalCount;
// 当前页数据
private List<T> rows;
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public List<T> getRows() {
return rows;
}
public void setRows(List<T> rows) {
this.rows = rows;
}
}
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
# 流程分析
后端需要响应总记录数和当前页的数据两部分给前端,所以在BrandMapper
接口中需要定义两个方法:
selectByPage()
:查询当前页的数据的方法selectTotalCount()
:查询总记录的方法
整体流程如下:
# 分页查询后端实现
# 分页查询dao
方法实现
在BrandMapper
接口中定义selectByPage()
方法进行分页查询,代码:
/**
* 分页查询
* @param begin
* @param size
* @return
*/
List<Brand> selectByPage(@Param("begin") int begin, @Param("size") int size);
/**
* 查询总记录数
* @return
*/
int selectTotalCount();
2
3
4
5
6
7
8
9
10
11
12
13
<select id="selectByPage" resultMap="brandResultMap">
select
id,
brand_name,
company_name,
ordered,
description,
status
from
tb_brand
limit #{begin}, #{size}
</select>
<select id="selectTotalCount" resultType="java.lang.Integer">
select count(*) from tb_brand
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 分页查询service
方法实现
在BrandService
接口中定义selectByPage()
分页查询数据的业务逻辑方法
/**
* 分页查询
* @param currentPage
* @param pageSize
* @return
*/
PageBean<Brand> selectByPage(int currentPage, int pageSize);
2
3
4
5
6
7
在BrandServiceImpl
类中重写selectByPage()
方法,并进行实现
@Override
public PageBean<Brand> selectByPage(int currentPage, int pageSize) {
// 1. 获取SqlSession
SqlSession sqlSession = factory.openSession(true);
// 2. 获取BrandMapper
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
// 3. 计算开始索引
int begin = (currentPage - 1) * pageSize;
// 4. 计算查询条目数
int size = pageSize;
// 5. 查询当前页数据
List<Brand> brands = mapper.selectByPage(begin, size);
// 6. 查询总记录数
int totalCount = mapper.selectTotalCount();
// 7. 封装PageBean对象
PageBean<Brand> pageBean = new PageBean<>();
pageBean.setRows(brands);
pageBean.setTotalCount(totalCount);
// 8. 是否资源
sqlSession.close();
// 9. 返回结果
return pageBean;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 分页查询servlet
实现
在 BrandServlet
类中定义 selectByPage()
方法。而该方法的逻辑如下:
- 获取页面提交的
当前页码
和每页显示条目数
两个数据。这两个参数是在 url 后进行拼接的,格式是url?currentPage=1&pageSize=5
。 获取这样的参数需要使用requet.getparameter()
方法获取。 - 调用
service
的selectByPage()
方法进行分页查询的业务逻辑处理 - 将查询到的数据转换为 json 格式的数据
- 响应
json
数据
servlet 中 selectByPage()
方法代码实现如下:
public void selectByPage(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 1. 接收数据
String _currentPage = request.getParameter("currentPage");
String _pageSize = request.getParameter("pageSize");
int currentPage = Integer.parseInt(_currentPage);
int pageSize = Integer.parseInt(_pageSize);
// 2. 调用service查询
PageBean<Brand> pageBean = brandService.selectByPage(currentPage, pageSize);
// 3. 转为json
String json = JSON.toJSONString(pageBean);
// 4. 响应
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 分页查询测试
在浏览器地址栏输入http://localhost/brand-case/brand/selectByPage?currentPage=1&pageSize=5,查询到如下数据:
{
"rows": [
{
"brandName": "华为",
"companyName": "华为技术有限公司",
"description": "万物互联",
"id": 1,
"ordered": 100,
"status": 1,
"statusStr": "启用"
},
{
"brandName": "小米",
"companyName": "小米科技有限公司",
"description": "are you ok",
"id": 2,
"ordered": 50,
"status": 1,
"statusStr": "启用"
},
{
"brandName": "格力",
"companyName": "格力电器股份有限公司",
"description": "让世界爱上中国造",
"id": 3,
"ordered": 30,
"status": 1,
"statusStr": "启用"
},
{
"brandName": "阿里巴巴",
"companyName": "阿里巴巴集团控股有限公司",
"description": "买买买",
"id": 4,
"ordered": 10,
"status": 1,
"statusStr": "启用"
},
{
"brandName": "腾讯",
"companyName": "腾讯计算机系统有限公司",
"description": "玩玩玩",
"id": 5,
"ordered": 50,
"status": 0,
"statusStr": "禁用"
}
],
"totalCount": 49
}
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
# 分页查询前端实现
# selectAll
代码改进
selectAll()
函数之前是查询所有数据,现在需要改成分页查询,请求路径应为http://localhost/brand-case/brand/selectByPage?currentPage=1&pageSize=5
,
而currentPage
和pageSize
是需要携带的参数,分别是当前页面和每页显示的条目数。
刚才我们对后端进行测试时可以看到响应回来的数据,所以在异步请求成功回调函数中需要给页面表格的数据模型赋值,整体代码如下:
var _this = this;
axios({
method:"post",
url:"http://localhost/brand-case/brand/selectByPage?currentPage=1&pageSize=5"
}).then(resp =>{
//设置表格数据
_this.tableData = resp.data.rows; // {rows:[],totalCount:100}
})
2
3
4
5
6
7
8
响应的数据中还有总记录数,要进行总记录数展示需要在页面绑定数据模型
注意
该数据模型totalCount
需要在Vue
对象中声明。
那异步请求的代码就可以优化为:
var _this = this;
axios({
method:"post",
url:"http://localhost:8080/brand-case/brand/selectByPage?currentPage=1&pageSize=5"
}).then(resp =>{
//设置表格数据
_this.tableData = resp.data.rows; // {rows:[],totalCount:100}
//设置总记录数
_this.totalCount = resp.data.totalCount;
})
2
3
4
5
6
7
8
9
10
而页面中分页组件给当前页码和每页显示条目数都绑定了数据模型
所以selectAll()
函数中发送异步请求的资源路径中不能将当前页码和每页显示条目数写死,代码需要优化为:
var _this = this;
axios({
method:"post",
url:"http://localhost:8080/brand-case/brand/selectByPage?currentPage="+this.currentPage+"&pageSize=" + this.pageSize
}).then(resp =>{
//设置表格数据
_this.tableData = resp.data.rows; // {rows:[],totalCount:100}
//设置总记录数
_this.totalCount = resp.data.totalCount;
})
2
3
4
5
6
7
8
9
10
# 改变每页条目数
当我们改变每页显示的条目数后,需要重新发送异步请求,下图是分页组件代码,@size-change
就是每页显示的条目数发生变化时会触发的事件
而该时间绑定了一个handleSizeChange
函数,整个逻辑如下:
handleSizeChange(val) { //我们选择的是 ‘5条/页’ 此值就是 5.而我们选择了 `10条/页` 此值就是 10
// 重新设置每页显示的条数
this.pageSize = val;
//调用 selectAll 函数重新分页查询数据
this.selectAll();
}
2
3
4
5
6
# 改变当前页码
当我们改变页码时,需要重新发送异步请求,下图就是分页组件代码,@current-change
就是页码发生变化时会触发的事件
而该时间绑定了一个handleSizeChange
函数,代码如下:
handleCurrentChange(val) { //val 就是改变后的页码
// 重新设置当前页码
this.currentPage = val;
//调用 selectAll 函数重新分页查询数据
this.selectAll();
}
2
3
4
5
6
# 条件查询
上图就是用来输入条件查询的条件数据的。要做条件查询功能,先明确以下三个问题
3个条件之间什么关系? 同时满足,所用 SQL 中多个条件需要使用 and 关键字连接
3个条件必须全部填写吗? 不需要。想根据哪儿个条件查询就写那个,所以这里需要使用动态 sql 语句
条件查询需要分页吗? 需要
根据上面三个问题的明确,我们就可以确定sql语句了:
整个条件分页查询流程如下
# 条件查询后端实现
# 条件查询dao
实现
在 BrandMapper
接口中定义 selectByPageAndCondition()
方法 和 selectTotalCountByCondition
方法,用来进行条件分页查询功能,方法如下:
/**
* 分页条件查询
* @param begin
* @param size
* @return
*/
List<Brand> selectByPageAndCondition(@Param("begin") int begin,@Param("size") int size,@Param("brand") Brand brand);
/**
* 根据条件查询总记录数
* @return
*/
int selectTotalCountByCondition(Brand brand);
2
3
4
5
6
7
8
9
10
11
12
13
参数:
begin
分页查询的起始索引size
分页查询的每页条目数brand
用来封装条件的对象
由于这是一个复杂的查询语句,需要使用动态sql;所以我们在映射配置文件中书写 sql 语句。brand_name
字段和 company_name
字段需要进行模糊查询,所以需要使用 %
占位符。映射配置文件中 statement 书写如下:
<!--查询满足条件的数据并进行分页-->
<select id="selectByPageAndCondition" resultMap="brandResultMap">
select *
from tb_brand
<where>
<if test="brand.brandName != null and brand.brandName != '' ">
and brand_name like #{brand.brandName}
</if>
<if test="brand.companyName != null and brand.companyName != '' ">
and company_name like #{brand.companyName}
</if>
<if test="brand.status != null">
and status = #{brand.status}
</if>
</where>
limit #{begin} , #{size}
</select>
<!--查询满足条件的数据条目数-->
<select id="selectTotalCountByCondition" resultType="java.lang.Integer">
select count(*)
from tb_brand
<where>
<if test="brandName != null and brandName != '' ">
and brand_name like #{brandName}
</if>
<if test="companyName != null and companyName != '' ">
and company_name like #{companyName}
</if>
<if test="status != null">
and status = #{status}
</if>
</where>
</select>
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
# 条件查询service
实现
在 BrandService
接口中定义 selectByPageAndCondition()
分页查询数据的业务逻辑方法
/**
* 分页条件查询
* @param currentPage
* @param pageSize
* @param brand
* @return
*/
PageBean<Brand> selectByPageAndCondition(int currentPage,int pageSize,Brand brand);
2
3
4
5
6
7
8
在 BrandServiceImpl
类中重写 selectByPageAndCondition()
方法,并进行业务逻辑实现
@Override
public PageBean<Brand> selectByPageAndCondition(int currentPage, int pageSize, Brand brand) {
//2. 获取SqlSession对象
SqlSession sqlSession = factory.openSession();
//3. 获取BrandMapper
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//4. 计算开始索引
int begin = (currentPage - 1) * pageSize;
// 计算查询条目数
int size = pageSize;
// 处理brand条件,模糊表达式
String brandName = brand.getBrandName();
if (brandName != null && brandName.length() > 0) {
brand.setBrandName("%" + brandName + "%");
}
String companyName = brand.getCompanyName();
if (companyName != null && companyName.length() > 0) {
brand.setCompanyName("%" + companyName + "%");
}
//5. 查询当前页数据
List<Brand> rows = mapper.selectByPageAndCondition(begin, size, brand);
//6. 查询总记录数
int totalCount = mapper.selectTotalCountByCondition(brand);
//7. 封装PageBean对象
PageBean<Brand> pageBean = new PageBean<>();
pageBean.setRows(rows);
pageBean.setTotalCount(totalCount);
//8. 释放资源
sqlSession.close();
return pageBean;
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
注意
brandName
和 companyName
属性值到时候需要进行模糊查询,所以前后需要拼接上 %
。
# 条件查询servlet
实现
在 BrandServlet
类中定义 selectByPageAndCondition()
方法。而该方法的逻辑如下:
获取页面提交的
当前页码
和每页显示条目数
两个数据。这两个参数是在url后进行拼接的,格式是url?currentPage=1&pageSize=5
。获取这样的参数需要使用requet.getparameter()
方法获取。获取页面提交的
条件数据
,并将数据封装到一个Brand对象中。由于这部分数据到时候是需要以 json 格式进行提交的,所以我们需要通过流获取数据,具体代码如下:// 获取查询条件对象 BufferedReader br = request.getReader(); String params = br.readLine();//json字符串 //转为 Brand Brand brand = JSON.parseObject(params, Brand.class);
1
2
3
4
5
6调用 service 的
selectByPageAndCondition()
方法进行分页查询的业务逻辑处理将查询到的数据转换为 json 格式的数据
响应 json 数据
servlet 中 selectByPageAndCondition()
方法代码实现如下:
/**
* 分页条件查询
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
public void selectByPageAndCondition(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
//1. 接收 当前页码 和 每页展示条数 url?currentPage=1&pageSize=5
String _currentPage = request.getParameter("currentPage");
String _pageSize = request.getParameter("pageSize");
int currentPage = Integer.parseInt(_currentPage);
int pageSize = Integer.parseInt(_pageSize);
// 获取查询条件对象
BufferedReader br = request.getReader();
String params = br.readLine();//json字符串
//转为 Brand
Brand brand = JSON.parseObject(params, Brand.class);
//2. 调用service查询
PageBean<Brand> pageBean = brandService.selectByPageAndCondition(currentPage,pageSize,brand);
//2. 转为JSON
String jsonString = JSON.toJSONString(pageBean);
//3. 写数据
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
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
# 条件查询前端实现
前端代码我们从以下几方面实现:
查询表单绑定查询条件对象模型
这一步在页面上已经实现了,页面代码如下:
点击查询按钮查询数据
从上面页面可以看到给
查询
按钮绑定了onSubmit()
函数,而在onSubmit()
函数中只需要调用selectAll()
函数进行条件分页查询。改进 selectAll() 函数
子页面加载完成后发送异步请求,需要携带当前页码、每页显示条数、查询条件对象。接下来先对携带的数据进行说明:
当前页码
和每页显示条数
这两个参数我们会拼接到 URL 的后面查询条件对象
这个参数需要以 json 格式提交给后端程序
修改
selectAll()
函数逻辑为var _this = this; axios({ method:"post", url:"http://localhost:8080/brand-case/brand/selectByPageAndCondition?currentPage="+this.currentPage+"&pageSize="+this.pageSize, data:this.brand }).then(function (resp) { //设置表格数据 _this.tableData = resp.data.rows; // {rows:[],totalCount:100} //设置总记录数 _this.totalCount = resp.data.totalCount; })
1
2
3
4
5
6
7
8
9
10
11
12
13
# 前端代码优化
咱们已经将所有的功能实现完毕。而针对前端代码中的发送异步请求的代码,如下
var _this = this;
axios({
method:"post",
url:"http://localhost:8080/brand-case/brand/selectByPageAndCondition?currentPage="+this.currentPage+"&pageSize="+this.pageSize,
data:this.brand
}).then(function (resp) {
//设置表格数据
_this.tableData = resp.data.rows; // {rows:[],totalCount:100}
//设置总记录数
_this.totalCount = resp.data.totalCount;
})
2
3
4
5
6
7
8
9
10
11
12
13
需要在成功的回调函数(也就是then
函数中的匿名函数)中使用this,都需要在外边使用 _this
记录一下 this
所指向的对象;因为在外边的 this
表示的是 Vue 对象,而回调函数中的 this
表示的不是 vue 对象。这里我们可以使用 ECMAScript6
中的新语法(箭头函数)来简化这部分代码,如上面的代码可以简化为:
axios({
method:"post",
url:"http://localhost:8080/brand-case/brand/selectByPageAndCondition?currentPage="+this.currentPage+"&pageSize="+this.pageSize,
data:this.brand
}).then((resp) => {
//设置表格数据
this.tableData = resp.data.rows; // {rows:[],totalCount:100}
//设置总记录数
this.totalCount = resp.data.totalCount;
})
2
3
4
5
6
7
8
9
10
11
箭头函数语法:
(参数) => {
逻辑代码
}
2
3
箭头函数的作用:
替换(简化)匿名函数。