以上是我们在综合案例要实现的功能。对数据的除了对数据的增删改查功能外,还有一些复杂的功能,如 批量删除 、 分页查询 、 条件查询 等功能
批量删除
功能:每条数据前都有复选框,当我选中多条数据并点击 批量删除 按钮后,会发送请求到后端并删除数据库中指定的多条数据。
分页查询
功能:当数据库中有很多数据时,我们不可能将所有的数据展示在一页里,这个时候就需要分页展示数据。
条件查询
功能:数据库量大的时候,我们就需要精确的查询一些想看到的数据,这个时候就需要通过条件查询。
这里的 修改品牌
和 删除品牌
功能在课程上不做讲解,留作同学来下的练习。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>brand-demoartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>8999port>
configuration>
plugin>
plugins>
build>
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.62version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.34version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.2version>
<scope>providedscope>
dependency>
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>taglibsgroupId>
<artifactId>standardartifactId>
<version>1.1.2version>
dependency>
dependencies>
project>
mybatis配置文件 mybatis-config.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.lpl.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/hsp_db02?useSSL=false&useServerPrepStmts=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="lpl"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.lpl.mapper"/>
mappers>
configuration>
数据库测试表 tb_brand, 多加几份数据
-- 添加数据
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)
;
前端页面:
参考 Element学习–实现好看的操作页面
dao层和之前的一样
用注解的形式写sql语句
public interface BrandMapper {
/**
* 修改
* @param brand
*/
@Update("update tb_brand set brand_name = #{brandName}, company_name = #{companyName}, " +
"ordered = #{ordered}, description = #{description}, status = #{status} " +
"where id = #{id}")
void update(Brand brand);
/**
* 按id查询
* @param id
* @return
*/
@Select("select * from tb_brand where id = #{id}")
@ResultMap("brandResultMap")
Brand selectById(int id);
/**
* 添加
* @param brand
*/
@Insert("insert into tb_brand values " +
"(null, #{brandName}, #{companyName}, #{ordered}, #{description}, #{status})")
void add(Brand brand);
/**
* 查询所有
* @return
*/
@Select("select * from tb_brand")
@ResultMap("brandResultMap")
List<Brand> selectAll();
}
解决数据库表的字段名和pojo实体类的属性名不一致,导致前端查询不全的问题,resultMap
在 com/lpl/mapper/BrandMapper.xml 中:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lpl.mapper.BrandMapper">
<resultMap id="brandResultMap" type="brand">
<result column="brand_name" property="brandName">result>
<result column="company_name" property="companyName">result>
resultMap>
mapper>
service层和之前不同
要利用多态,做一个解耦操作
,创建接口BrandService接口, 把之前的Service变成实现类BrandServiceImpl:
这里使用多态
是因为方便我们后期解除 Servlet 和 service 的耦合。从上面的代码我们可以看到 SelectAllServlet 类
和 BrandServiceImpl 类之间是耦合在一起的,如果后期 BrandService 有其它更好的实现类(例如叫BrandServiceImpl22 ),那就需要修改 SelectAllServlet 类中的代码。后面我们学习了 Spring 框架后就可以解除
SelectAllServlet 类和红色框括起来的代码耦合。而现在咱们还做不到解除耦合,在这里只需要理解为什么定义接口即
可。
service接口
public interface BrandService {
/**
* 查询所有
* @return
*/
List<Brand> selectAll();
/**
* 更新
* @param brand
*/
void update(Brand brand);
/**
* 添加
* @param brand
*/
void add(Brand brand);
/**
* 按id查询
* @param id
* @return
*/
Brand selectById(int id);
}
service实现类
public class BrandServiceImpl implements BrandService {
//1.获取 sqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
/**
* 修改
* @param brand
*/
public void update(Brand brand) {
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//调用方法
brandMapper.update(brand);
//sql添加语句, 记得提交事务!!
sqlSession.commit();
//释放资源
sqlSession.close();
}
/**
* 按id查询
* @param id
* @return
*/
public Brand selectById(int id) {
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//调用方法
Brand brand = brandMapper.selectById(id);
//释放资源
sqlSession.close();
return brand;
}
/**
* 添加
* @param brand
*/
public void add(Brand brand){
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//调用方法
brandMapper.add(brand);
//sql添加语句, 记得提交事务!!
sqlSession.commit();
//释放资源
sqlSession.close();
}
/**
* 查询所有
* @return
*/
public List<Brand> selectAll() {
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//调用 BrandMapper.selectAll()
List<Brand> brands = brandMapper.selectAll();
//关闭资源
sqlSession.close();
return brands;
}
}
web层
@WebServlet("/selectAllServlet")
public class SelectAllServlet extends HttpServlet {
//brandService 可能多次用到, 把它放到成员变量的位置
private BrandService brandService = new BrandServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.调用 BrandService 完成查询
List<Brand> brands = brandService.selectAll();
//将集合转换为JSON 序列化
String jsonString = JSON.toJSONString(brands);
//响应数据
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
参考 Element学习–实现好看的操作页面
前端需要在页面加载完毕后发送 ajax 请求,所以发送请求的逻辑应该放在 mounted() 钩子函数中。而响应回来的数据需要赋值给表格绑定的数据模型,从下图可以看出表格绑定的数据模型是 tableData:
加入代码:
mounted(){
//页面加载完成之后,发送异步请求
var _this = this; //this不能直接在生命周期里使用
axios({
method:"get",
url:"http://localhost:8999/brand-demo/selectAllServlet"
}).then(function (resp){
_this.tableData = resp.data;
})
},
页面发送请求时,需要将输入框输入的内容提交给后端程序,而这里是以 json 格式进行传递的。而具体的数据格式如下:
web层 servlet
@WebServlet("/addServlet")
public class AddServlet extends HttpServlet {
//brandService 可能多次用到, 把它放到成员变量的位置
private BrandService brandService = new BrandServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决post 请求 中文乱码
request.setCharacterEncoding("UTF-8");
/* 使用Axios + JSON 完成品牌列表数据查询和添加。*/
//获取post请求体数据
BufferedReader br = request.getReader();
String params = br.readLine();
//将JSON字符串转为Java对象
Brand brand = JSON.parseObject(params, Brand.class);
System.out.println(brand);
//调用service进行添加
brandService.add(brand);
//响应成功标识
response.getWriter().write("success");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
上图左边是页面效果,里面的 提交 按钮可以通过上图右边看出绑定了一个 单击事件,而该事件绑定的是 addBrand 函数,
所以添加数据功能的逻辑代码应该写在 addBrand() 函数中。在此方法中需要发送异步请求并将表单中输入的数据作为参数
进行传递。如下:
var _this = this; //this不能直接使用
addBrand(){
// console.log(this.brand);
//发送ajax请求, 添加数据
var _this = this; //this不能直接使用
axios({
method: "post",
url:"http://localhost:8999/brand-demo/addServlet",
data: _this.brand
}).then(function (resp) {......}
在 then 函数中的匿名函数是成功后的回调函数,而 resp.data 就可以获取到响应回来的数据,如果值是 success 表示数
据添加成功。成功后我们需要做一下逻辑处理:
then(function (resp) {
if (resp.data.toString() == "success"){
//添加成功
//关闭添加的对话窗口
_this.dialogVisible = false;
//重新查询数据
_this.selectAll();
//弹出消息给用户提示添加成功
_this.$message({
message: '恭喜你,添加成功',
type: 'success'
});
}
})
对比
Web 层的 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(...) {}
}
而我们知道发送请求 servlet
, tomcat
会自动的调用 service()
方法,之前我们在自定义的 servlet 中重写 doGet()
方法和 doPost() 方法,当我们访问该 servlet 时会根据请求方式将请求分发给 doGet() 或者 doPost() 方法,如下图:
那么我们也可以仿照这样请求分发的思想,在 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 {
//进行请求的分发
}
}
BrandServlet 定义就需要修改为如下:
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
//用户实现分页查询
public void selectAll(...) {}
//添加企业信息
public void add(...) {}
//修改企业信息
public void update(...) {}
//删除企业信息
public void delete(...) {}
}
那么如何在 BaseServlet 中调用对应的方法呢?比如查询所有就调用 selectAll() 方法。
可以规定在发送请求时,请求资源的二级路径(/brandServlet/selectAll)和需要调用的方法名相同,
如:
查询所有数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/selectAll
添加数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/add
修改数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/update
删除数据的路径以后就需要写成: http://localhost:8080/brand-case/brandServlet/delete
这样的话,在 BaseServlet 中就需要获取到资源的二级路径作为方法名,然后调用该方法
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();
}
通过上面代码发现根据方法名获取对应方法的 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) {}
}
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对象
Method method = null;
try {
method = cls.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
//2.3 执行方法
try {
method.invoke(this,req,resp);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
定义了 BaseServlet 后,针对品牌模块我们定义一个 BrandServlet 的 Servlet,并使其继承 BaseServlet 。在
BrandServlet 中定义 以下功能的方法:
@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
//brandService 可能多次用到, 把它放到成员变量的位置
private BrandService brandService = new BrandServiceImpl();
//用户实现分页查询
public void selectAll(HttpServletRequest request, HttpServletResponse response) throws IOException {
//1.调用 BrandService 完成查询
List<Brand> brands = brandService.selectAll();
/* 使用Axios + JSON 完成品牌列表数据查询和添加。*/
//将集合转换为JSON 序列化
String jsonString = JSON.toJSONString(brands);
//响应数据
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
//添加企业信息
public void add(HttpServletRequest request, HttpServletResponse response) throws Exception {
//解决post 请求 中文乱码
request.setCharacterEncoding("UTF-8");
/* 使用Axios + JSON 完成品牌列表数据查询和添加。*/
//获取post请求体数据
BufferedReader br = request.getReader();
String params = br.readLine();
//将JSON字符串转为Java对象
Brand brand = JSON.parseObject(params, Brand.class);
System.out.println(brand);
//调用service进行添加
brandService.add(brand);
//响应成功标识
response.getWriter().write("success");
}
}
//查询所有数据的方法
selectAll(){
var _this = this; //this不能直接在生命周期里使用
axios({
method:"get",
url:"http://localhost:8999/brand-demo/brand/selectAll"
}).then(function (resp){
_this.tableData = resp.data;
})
},
//添加数据
addBrand(){
// console.log(this.brand);
//发送ajax请求, 添加数据
var _this = this; //this不能直接使用
axios({
method: "post",
url:"http://localhost:8999/brand-demo/brand/add",
data: _this.brand
}).then(function (resp) {
if (resp.data.toString() == "success"){
//添加成功
//关闭添加的对话窗口
_this.dialogVisible = false;
//重新查询数据
_this.selectAll();
_this.$message({
message: '恭喜你,添加成功',
type: 'success'
});
}
})
},
dao层
在BrandMapper中:
/**
* 按id查询
* @param id
* @return
*/
@Select("select * from tb_brand where id = #{id}")
@ResultMap("brandResultMap")
Brand selectById(int id);
service层
接口BrandService中:
/**
* 按id查询
* @param id
* @return
*/
Brand selectById(int id);
实现类BrandServiceImpl中:
/**
* 按id查询
* @param id
* @return
*/
public Brand selectById(int id) {
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//调用方法
Brand brand = brandMapper.selectById(id);
//释放资源
sqlSession.close();
return brand;
}
web层
BeandServlet中:
//按Id查询
public void selectById(HttpServletRequest request, HttpServletResponse response) throws IOException {
//接收数据
String id = request.getParameter("id");
//1.调用 BrandService 完成查询
Brand brand = brandService.selectById(Integer.parseInt(id));
/* 使用Axios + JSON 完成品牌列表数据查询和添加。*/
//将集合转换为JSON 序列化
String jsonString = JSON.toJSONString(brand);
//响应数据
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
问题分析:
从这个关系图可得看出问题,比如:
当需要修改信息,即点击修改按钮时,selectById() 回显数据会修改 数据模型brand;此时,搜索表单 和 分页工具 会受到影响。
解决:做一个数据模型brand_update,与修改的操作绑定
,如下图:
同样分析可知,新增操作同样存在这样的问题… 也可以用以数据模型 brand_add来解决
具体代码如下:
在methods中:
//更改信息-回显数据
selectById(id){
console.log("selectById...")
console.log(id)
//发送ajax请求, 添加数据
var _this = this; //this不能直接使用
axios({
method: "get",
url:"http://localhost:8999/brand-demo/brand/selectById?id=" + id,
}).then(function (resp) {
//添加成功
//开启修改的对话窗口
_this.dialogVisible_update = true;
//在对话框回显数据
console.log(" selectById 的 resp.data = ");
console.log(resp.data);
_this.brand_copy = resp.data;
})
},
在data中:
//是否展示修改数据对话框
dialogVisible_update: false,
//品牌模型数据副本
brand_copy: {
status: '',
brandName: '',
companyName: '',
id: '',
ordered:'',
description:''
},
在html中:
给“修改按钮”添加点击事件,注意里面每行 id 的获取!!!!!
<el-table-column
label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" @click="selectById(scope.row.id)" >修改el-button>
<el-button type="danger" >删除el-button>
template>
el-table-column>
创建修改对话框表单
:visible.sync="dialogVisible_update"
这是为了区别 添加对话框 的 :visible.sync="dialogVisible_add"
<el-dialog
title="修改品牌"
:visible.sync="dialogVisible_update"
width="30%">
<el-form ref="form" :model="brand_copy" label-width="80px">
<el-form-item label="品牌名称">
<el-input v-model="brand_copy.brandName"
value="brand.brandName"
>
el-input>
el-form-item>
<el-form-item label="企业名称">
<el-input v-model="brand_copy.companyName">el-input>
el-form-item>
<el-form-item label="排序">
<el-input v-model="brand_copy.ordered">el-input>
el-form-item>
<el-form-item label="备注">
<el-input type="textarea" v-model="brand_copy.description">el-input>
el-form-item>
<el-form-item label="状态">
<el-switch v-model="brand_copy.status"
:active-value="1"
:inactive-value="0"
active-color="#13ce66"
inactive-color="#ff4949">
el-switch>
el-form-item>
<el-form-item>
<el-button type="primary" >提交el-button>
<el-button >取消el-button>
el-form-item>
el-form>
el-dialog>
dao层
在brandMapper中:
/**
* 修改
* @param brand
*/
@Update("update tb_brand set brand_name = #{brandName}, company_name = #{companyName}, " +
"ordered = #{ordered}, description = #{description}, status = #{status} " +
"where id = #{id}")
void update(Brand brand);
service层
接口BrandService中:
/**
* 更新
* @param brand
*/
void update(Brand brand);
实现类BrandServiceImpl中:
/**
* 修改
* @param brand
*/
public void update(Brand brand) {
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//调用方法
brandMapper.update(brand);
//sql添加语句, 记得提交事务!!
sqlSession.commit();
//释放资源
sqlSession.close();
}
web层
在BrandServlet中添加update()方法:
//修改信息
public void update(HttpServletRequest request, HttpServletResponse response) throws Exception{
//解决post 请求 中文乱码
request.setCharacterEncoding("UTF-8");
//获取post请求体数据
BufferedReader br = request.getReader();
String params = br.readLine();
//将JSON字符串转为Java对象
Brand brand = JSON.parseObject(params, Brand.class);
System.out.println(brand);
//调用service进行修改
brandService.update(brand);
//响应成功标识
response.getWriter().write("success");
}
methods中,写update()方法:
//修改功能-更改信息
update(){
//发送ajax请求, 添加数据
var _this = this; //this不能直接使用
axios({
method: "post",
url:"http://localhost:8999/brand-demo/brand/update",
data: _this.brand_copy
}).then(function (resp) {
if (resp.data.toString() == "success"){
//修改成功
//关闭修改的对话窗口
_this. dialogVisible_update = false;
//重新查询数据
_this.selectByPage();
_this.$message({
message: '恭喜你,添加成功',
type: 'success'
});
}
})
},
html中,给修改对话框表单添加响应点击事件 @click="update"
@click=" dialogVisible_update = false"
<el-form-item>
<el-button type="primary" @click="update">提交el-button>
<el-button @click=" dialogVisible_update = false">取消el-button>
el-form-item>
dao层:
/**
* 按id删除
* @param id
*/
@Delete("delete from tb_brand where id = #{id}")
void deleteById(int id);
sevice层
接口BrandService中:
/**
* 按id删除
* @param id
*/
void deleteById(int id);
实现类BrandServiceImpl中:
@Override
public void deleteById(int id) {
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//调用方法
brandMapper.deleteById(id);
//sql添加语句, 记得提交事务!!
sqlSession.commit();
//释放资源
sqlSession.close();
}
web层:
//删除企业信息
public void deleteById(HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取id
String id = request.getParameter("id");
//调用service进行删除
brandService.deleteById(Integer.parseInt(id));
//响应成功标识
response.getWriter().write("success");
}
html中:
给删除按钮绑定点击事件, @click="deleteById(scope.row.id)
同样注意Id的获取==>scope.row.id
<el-table-column
label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" @click="selectById(scope.row.id)" >修改el-button>
<el-button type="danger" @click="deleteById(scope.row.id)">删除el-button>
template>
el-table-column>
methods中写deleteById()方法:
//删除
deleteById(id){
// 弹出确认删除提示框
this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//用户点击确认按钮
//发送异步请求
var _this = this; //this不能直接在生命周期里使用
axios({
method: "get",
url:"http://localhost:8999/brand-demo/brand/deleteById?id=" + id,
}).then(function (resp) {
if (resp.data.toString() == "success"){
//删除成功
//重新查询数据
// _this.selectAll();
_this.selectByPage();
_this.$message({
message: '数据删除成功',
type: 'success'
});
}
})
}).catch(() => {
//用户点击取消按钮
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
如上图所示点击多条数据前的复选框就意味着要删除这些数据,而点击了 批量删除 按钮后,需要让用户确认一下,因为有可能是用户误操作的,当用户确定后需要给后端发送请求并携带者需要删除数据的多个id值,后端程序删除数据库中的数据。
具体的流程如下:
注意:
前端发送请求时需要将要删除的多个id值以json格式提交给后端,而该json格式数据如下:
[1,2,3,4]
dao层
在 BrandMapper 接口中定义 deleteByIds() 添加方法,由于这里面要用到动态 sql ,属于复杂的sql操作,建议使用映射配置文件来写sql。
接口方法声明如下:
/**
* 批量删除
* @param ids
*/
void deleteByIds(@Param("ids") int[] ids);
在 BrandMapper.xml 映射配置文件中添加 statement:
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
service方法实现
在 BrandService 接口中定义 deleteByIds() 批量删除的业务逻辑方法:
/**
* 批量删除
* @param ids
*/
void deleteByIds( int[] ids);
在 BrandServiceImpl 类中重写 deleteByIds() 方法,并进行业务逻辑实现:
增删改操作要记得提交事务
@Override
public void deleteByIds(int[] ids) {
//2. 获取SqlSession对象
SqlSession sqlSession = factory.openSession();
//3. 获取BrandMapper
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
//4. 调用方法
mapper.deleteByIds(ids);
sqlSession.commit();//提交事务
//5. 释放资源
sqlSession.close();
}
servlet实现
在 BrandServlet 类中定义 deleteByIds() 方法。而该方法的逻辑如下:
servlet 中 deleteByIds() 方法代码实现如下:
public void deleteByIds(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
//1. 接收数据 json [1,2,3]
BufferedReader br = request.getReader();
String params = br.readLine();//json字符串
//转为 int[]
int[] ids = JSON.parseObject(params, int[].class);
//2. 调用service添加
brandService.deleteByIds(ids);
//3. 响应成功的标识
response.getWriter().write("success");
}
获取选中的id值
从上图可以看出表格复选框绑定了一个 selection-change 事件,该事件是当选择项发生变化时会触发。该事件绑定了
handleSelectionChange 函数,而该函数有一个参数 val ,该参数是获取选中行的数据,如下:
而我们只需要将所有选中数据的id值提交给服务端即可,获取id的逻辑我们书写在 批量删除 按钮绑定的函数中。
在 批量删除 按钮绑定单击事件,并给绑定触发时调用的函数,如下:
并在Vue对象中的 methods 中定义 deleteByIds() 函数,在该函数中从 multipleSelection 数据模型中获取所选数据的
id值。要完成这个功能需要在 Vue 对象中定义一个数据模型 selectedIds:[] ,在 deleteByIds() 函数中遍历
multipleSelection 数组,并获取到每一个所选数据的id值存储到 selectedIds 数组中,代码实现如下:
//批量删除
deleteByIds() {
//创建id数组 [1,2,3] -->selectedIds
//复选框选中数据集合 multipleSelection 存储的是被选中的Brand对象
for (let i = 0; i < this.multipleSelection.length; i++) {
this.selectedIds[i] = this.multipleSelection[i].id;
console.log(this.selectedIds[i]);
}
发送异步请求
使用 axios 发送异步请求并经上一步获取到的存储所有的 id 数组作为请求参数
//发送异步请求
var _this = this; //this不能直接在生命周期里使用
axios({
method: "post",
url:"http://localhost:8999/brand-demo/brand/deleteByIds",
data: _this.selectedIds
}).then(function (resp) {
if (resp.data.toString() == "success"){
//删除成功
//重新查询数据
_this.selectAll();
_this.$message({
message: '数据删除成功',
type: 'success'
});
}
})
确定框实现
由于删除操作是比较危险的;有时候可能是由于用户的误操作点击了 批量删除 按钮,所以在点击了按钮后需要先给用户确认提示。而确认框在 elementUI 中也提供了,如下图
而在点击 确定 按钮后需要执行之前删除的逻辑。因此前端代码实现如下:
// 批量删除
deleteByIds(){
// 弹出确认提示框
this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//用户点击确认按钮
//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. 发送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'
});
}
})
}).catch(() => {
//用户点击取消按钮
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
我们之前做的 查询所有 功能中将数据库中所有的数据查询出来并展示到页面上,试想如果数据库中的数据有很多(假设有十
几万条)的时候,将数据全部展示出来肯定不现实,那如何解决这个问题呢?几乎所有的网站都会使用分页解决这个问题。
每次只展示一页的数据,比如一页展示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;
前后端数据分析
分页查询功能时候比较复杂的,所以我们要先分析清楚以下两个问题:
前端需要传递什么参数给后端
根据上一步对分页查询 SQL 语句分析得出,前端需要给后端两个参数
上图是分页查询页面展示的效果,从上面我们可以看出需要响应以下联股份数据
而这两部分需要封装到 PageBean 对象(或者说Pojo)中,并将该对象转换为 json 格式的数据响应回给浏览器
通过上面的分析我们需要先在 pojo 包下创建 PageBean 类,为了做到通过会将其定义成泛型类,代码如下:
//分页查询的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;
}
}
流程分析
后端需要响应 总记录数 和 当前页的数据 两部分数据给前端,所以在 BrandMapper 接口中需要定义两个方法:
selectByPage() :查询当前页的数据的方法
selectTotalCount() :查询总记录的方法
整体流程如下:
dao方法实现
在 BrandMapper 接口中定义 selectByPage() 方法进行分页查询,代码如下:
/**
* 分页查询
* @param begin
* @param size
* @return
*/
@Select("select * from tb_brand limit #{begin} , #{size}")
@ResultMap("brandResultMap")
List<Brand> selectByPage(@Param("begin") int begin,@Param("size") int size);
在 BrandMapper 接口中定义 selectTotalCount() 方法进行统计记录数,代码如下:
/**
* 查询总记录数
* @return
*/
@Select("select count(*) from tb_brand ")
int selectTotalCount();
service方法实现
在 BrandService 接口中定义 selectByPage() 分页查询数据的业务逻辑方法
/**
* 分页查询
* @param currentPage 当前页码
* @param pageSize 每页展示条数
* @return
*/
PageBean<Brand> selectByPage(int currentPage, int pageSize);
在 BrandServiceImpl 类中重写 selectByPage() 方法,并进行业务逻辑实现
@Override
/**
* 分页查询
* @param currentPage 当前页码
* @param pageSize 每页展示条数
* @return
*/
public PageBean<Brand> selectByPage(int currentPage, int pageSize) {
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//计算开始索引
int begin = (currentPage - 1) * pageSize;
//每页展示条数
int size = pageSize;
System.out.println("begin = " + begin);
System.out.println("size = " + size);
//调用方法
List<Brand> rows = brandMapper.selectByPage(begin, size);
int totalCount = brandMapper.selectTotalCount();
//封装到 PageBean
PageBean<Brand> brandPageBean = new PageBean<>();
brandPageBean.setRows(rows);
brandPageBean.setTotalCount(totalCount);
System.out.println("brandPageBean = " + brandPageBean);
// //sql添加语句, 记得提交事务!!
// sqlSession.commit();
//释放资源
sqlSession.close();
return brandPageBean;
}
servlet实现
在 BrandServlet 类中定义 selectByPage() 方法。而该方法的逻辑如下:
servlet 中 selectByPage() 方法代码实现如下:
//分页查询
public void selectByPage(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("BrandServletd的 selectByPage 被调用....");
//解决post 请求 中文乱码
request.setCharacterEncoding("UTF-8");
//1.接收参数 当前页码 和 每页展示条数 url?currentPage=1&pageSize=8
String _currentPage = request.getParameter("currentPage");
String _pageSize = request.getParameter("pageSize");
//转类型
int currentPage = Integer.parseInt(_currentPage);
int pageSize = Integer.parseInt(_pageSize);
//2.调用service进行查询
PageBean<Brand> brandPageBean = brandService.selectByPage(currentPage, pageSize);
//3.写数据
//将Java对象转为json
String jsonString = JSON.toJSONString(brandPageBean);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
再写一个 selectByPage() 方法
请求路径应改为 http://localhost:8999/brand-
demp/brand/selectByPage?currentPage=1&pageSize=5 ,而 currentPage 和 pageSize 是需要携带的参数,分别是 当前页码 和 每页显示的条目数。
要在页面中分页组件给 当前页码
总记录数
和 每页显示的条目数
都绑定了数据模型
//分页查询
selectByPage(){
var _this = this; //this不能直接在生命周期里使用
axios({
method:"get",
url:"http://localhost:8999/brand-demo/brand/selectByPage?" +
"currentPage=" + this.currentPage + "&pageSize=" + this.pageSize
}).then(function (resp){ //resp.data = {"rows":[...],"totalCount":46}}
//设置表格数据
_this.tableData = resp.data.rows;
//设置总记录数
_this.totalCount = resp.data.totalCount;
})
},
注意改调用时机:
mounted(){
//页面加载完成之后,发送异步请求
// this.selectAll();
this.selectByPage();
},
改变每页条目数
当我们改变每页显示的条目数后,需要重新发送异步请求。而下图是分页组件代码, @size-change 就是每页显示的条目数
发生变化时会触发的事件
而该事件绑定了一个 handleSizeChange 函数,整个逻辑如下:
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
//我们选择的是 ‘5条/页’ 此值就是 5.而我们选择了 `10条/页` 此值就是 10
// 重新设置每页显示的条数
this.pageSize = val;
//调用 selectByPage 函数重新分页查询数据
this.selectByPage();
},
改变当前页码
当我们改变页码时,需要重新发送异步请求。而下图是分页组件代码, @current-change 就是页码发生变化时会触发的事件
而该事件绑定了一个 handleSizeChange 函数,整个逻辑如下:
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
//val 就是改变后的页码
// 重新设置当前页码
this.currentPage = val;
//调用 selectByPage 函数重新分页查询数据
this.selectByPage();
},
上图就是用来输入条件查询的条件数据的。要做条件查询功能,先明确以下三个问题
dao实现
在 BrandMapper 接口中定义 selectByPageAndCondition() 方法 和 selectTotalCountByCondition 方法,用来进行
条件分页查询功能,方法如下:
/**
* 根据条件进行查询的总记录数
* @return
*/
int selectTotalCountByCondition(Brand brand);
/**
* 根据条件进行分页查询
* @param begin 分页查询的起始索引
* @param size 分页查询的每页条目数
* @param brand 用来封装条件的对象
* @return
*/
@ResultMap("brandResultMap")
List<Brand> selectByPageAndCondition(@Param("beign") int begin,
@Param("size")int size, @Param("brand")Brand brand);
由于这是一个复杂的查询语句,需要使用动态sql;所以我们在映射配置文件中书写 sql 语句。 brand_name 字段和
company_name 字段需要进行模糊查询,所以需要使用 % 占位符。映射配置文件中 statement 书写如下:
<select id="selectByPageAndCondition" resultMap ="brandResultMap">
select *
from tb_brand
<where>
<if test="brand.companyName != null and brand.companyName != '' ">
and company_name like #{brand.companyName}
if>
<if test="brand.brandName != null and brand.brandName != '' ">
and brand_name like #{brand.brandName}
if>
<if test="brand.status != null">
and status = #{brand.status}
if>
where>
limit #{beign}, #{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>
service实现
在 BrandService 接口中定义 selectByPageAndCondition() 分页查询数据的业务逻辑方法
/**
* 根据条件进行分页查询
* @param currentPage
* @param pageSize
* @param brand
* @return
*/
PageBean<Brand> selectByPageAndCondition(int currentPage, int pageSize, Brand brand);
在 BrandServiceImpl 类中重写 selectByPageAndCondition() 方法,并进行业务逻辑实现
@Override
/**
* 根据条件进行分页查询
* @param currentPage
* @param pageSize
* @param brand
* @return
*/
public PageBean<Brand> selectByPageAndCondition(int currentPage, int pageSize, Brand brand){
//2.获取 sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取 brandMapper
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//计算开始索引
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 + "%");
}
//调用方法
List<Brand> rows = brandMapper.selectByPageAndCondition(begin, size, brand);
int totalCount = brandMapper.selectTotalCountByCondition(brand);
//封装到 PageBean
PageBean<Brand> brandPageBean = new PageBean<>();
brandPageBean.setRows(rows);
brandPageBean.setTotalCount(totalCount);
// System.out.println("brandPageBean = " + brandPageBean);
// //sql添加语句, 记得提交事务!!
// sqlSession.commit();
//释放资源
sqlSession.close();
return brandPageBean;
}
注意: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);
调用 service 的 selectByPageAndCondition() 方法进行分页查询的业务逻辑处理
将查询到的数据转换为 json 格式的数据
响应 json 数据
servlet 中 selectByPageAndCondition() 方法代码实现如下:
//分页条件查询
public void selectByPageAndCondition(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("BrandServletd的 selectByPage 被调用....");
//解决post 请求 中文乱码
request.setCharacterEncoding("UTF-8");
//1.1 接收参数 当前页码 和 每页展示条数 url?currentPage=1&pageSize=8
String _currentPage = request.getParameter("currentPage");
String _pageSize = request.getParameter("pageSize");
//转类型
int currentPage = Integer.parseInt(_currentPage);
int pageSize = Integer.parseInt(_pageSize);
//1.2 获取查询条件 即请求体中的数据
BufferedReader br = request.getReader();
String params = br.readLine();
//将JSON字符串转为Java对象
Brand brand = JSON.parseObject(params, Brand.class);
//2.调用service进行查询
PageBean<Brand> brandPageBean =
brandService.selectByPageAndCondition(currentPage, pageSize, brand);
//3.写数据
//将Java对象转为json
String jsonString = JSON.toJSONString(brandPageBean);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
前端代码我们从以下几方面实现:
查询表单绑定查询条件对象模型
改进 selectAll() 函数
子页面加载完成后发送异步请求,需要携带当前页码、每页显示条数、查询条件对象。接下来先对携带的数据进行说
明:
修改 selectByPage() 函数逻辑为
var _this = this;
axios({
method:"post",
url:"http://localhost:8999/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;
})