17、基于Mybaits、Vue、axios、Element-ui的JavaWeb项目

news2024/11/15 19:54:48

目录

1、项目功能介绍

​编辑

        

2、环境准备

创建项目

准备数据库

准备Mybatis核心配置文件

创建实体类与Mapper映射文件

补全项目结构

1、在pom.xml中导入相关依赖

2、导入axios、vue的js文件

3、导入Element-ui

3、查询所有功能的实现   

3.1、后端的实现

3.1.1、dao层方法的实现:

3.1.2、service方法的实现

3.1.3、servlet实现

3.1.4、测试后端程序

3.2、前端实现

3.2.1、创建brand.html页面

3.2.2、官网复制Element组件代码

4、添加功能的实现

 4.1、后端的实现

4.1.1、dao层方法的实现

4.1.2、serivce方法的实现

4.1.3、servlet的实现

4.2、前端实现

5、Servlet代码优化

5.1、问题引入

5.2、代码优化

5.2.1、后端代码的优化

 5.2.2、前端代码的优化

6、批量删除功能实现

6.1、后端实现

6.1.1、dao层代码的实现

​编辑

6.1.2、service方法实现

6.1.2、servlet的实现

6.2、前端实现

6.2.1、获取选择的id值

6.2.2、发送异步请求

 6.2.3、确认框提示

7、分页查询功能的实现

7.1、分析

7.1.1、分页查询sql

7.1.2、前后端数据的分析

7.1.3、流程分析

7.2、后端实现 

7.2.1、dao层方法的实现

7.2.2、service方法实现

7.2.3、servlet实现

7.2.4、测试

7.3、前端实现

7.3.1、selectAll()的改进

 7.3.2、改变每页条目数

7.3.3、 改变当前页码

 8、条件查询功能实现

8.1、后端实现

8.1.1、dao层实现

8.1.2、service的实现

8.1.3、servlet的实现

8.2、前端实现

 10、修改功能的实现

10.1、后端实现

10.1.1、dao层方法的实现

10.1.2、service方法的实现

10.1.3、servlet实现

10.2、前端实现


1、项目功能介绍

以上是我们在综合案例要实现的功能。对数据的除了对数据的增删改查功能外,还有一些复杂的功能,如 批量删除分页查询条件查询 等功能

批量删除 功能:每条数据前都有复选框,当我选中多条数据并点击 批量删除 按钮后,会发送请求到后端并删除数据库中指定的多条数据。

分页查询 功能:当数据库中有很多数据时,我们不可能将所有的数据展示在一页里,这个时候就需要分页展示数据。

条件查询 功能:数据库量大的时候,我们就需要精确的查询一些想看到的数据,这个时候就需要通过条件查询。

修改品牌:

删除品牌

        

2、环境准备

创建项目

1、创建Maven项目,如下

 

为项目起名 

 

 在pom.xml中加入如下,是这个模块变为web模块

 

 

 

 然后依次点击 Apply ,Ok

做完上述操作以后,项目结构如下:

准备数据库

创建一个名为 db2 的数据库,建表语句如下:

create database db2;

在db2 下准备数据库表,如下

-- 删除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);

建好的的表如下:

准备Mybatis核心配置文件

1、在resources目录下添加mybaits核心配置文件,去mybatis官方复制粘贴,然后修改即可,步骤如下:

1)创建 mybatis-config.xml

2)修改后的mybatis核心配置文件如下所示

  

创建实体类与Mapper映射文件

因为一个实体类对应这一个mapper映射文件,因此需要先创建实体类

1、创建实体类

根据数据库表tb_brand,我们创建一个Brand实体类

步骤:右击java -> New -> Java Class

        

根据数据库表 tb_brand 在实体类Brand中创建对应的属性及get、set方法,如下所示

代码如下:

package com.clear.pojo;

public class Brand {
    private Integer id;
    private String brandName;
    private String companyName;
    private Integer ordered;
    private String description;
    private Integer status;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBrandName() {
        return brandName;
    }

    public void setBrandName(String brandName) {
        this.brandName = brandName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public Integer getOrdered() {
        return ordered;
    }

    public void setOrdered(Integer ordered) {
        this.ordered = ordered;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "Brand{" +
                "id=" + id +
                ", brandName='" + brandName + '\'' +
                ", companyName='" + companyName + '\'' +
                ", ordered=" + ordered +
                ", description='" + description + '\'' +
                ", status=" + status +
                '}';
    }
}

 2、创建mapper映射文件

在创建mapper映射文件之前,在创建一个mapper接口,如下所示

步骤:右击java -> New -> Java Class -> 选择Interface 

 

 接下来在resources目录下创建一个与BrandMapper接口在同一目录下的映射文件(因为项目部署时mapper接口生成的class字节码文件需要与mapper映射文件xml在同一目录下

步骤视图如下:

 

 接着我们与创建mybatis核心配置时一样,依然去myabtis官网复制粘贴,然后修改,修改后的映射文件如下所示:

 紧接着去修改mybatis核心配置文件中mapper标签 中 resource属性的值,如下所示:

补全项目结构

如下所示

        

1、在pom.xml中导入相关依赖

需要连接MySQL——导入MySQL相关依赖

使用了mybatis简化JDBC——导入mybatis依赖

需要使用JSON在前后端传递数据——导入fastjson

是Java的web项目——导入servlet(注意scope为provided)

为了方便启动服务器——导入tomcat7插件

pom.xml如下所示

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>brand-case</artifactId>
    <version>1.0-SNAPSHOT</version>


    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--servlet-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
            </plugin>
        </plugins>
    </build>

</project>

2、导入axios、vue的js文件

3、导入Element-ui

 至此,基本的环境就准备的差不多了,如果还需要其他的,后续在补充

3、查询所有功能的实现   

如上图所示是查询所有品牌数据在页面展示的效果。要实现这个功能,要先搞明白如下问题:

1)什么时候发送异步请求?

页面加载完毕后就需要在页面上看到所有的品牌数据。所以在 mounted() 这个构造函数中写发送异步请求的代码。

2)请求需要携带参数吗?

查询所有功能不需要携带什么参数。

3)响应的数据格式是什么样?

后端是需要将 List<Brand> 对象转换为 JSON 格式的数据并响应回给浏览器。响应数据格式如下:

整体流程如下:

 

我们先实现后端程序,然后再实现前端程序。  

3.1、后端的实现

3.1.1、dao层方法的实现:

在com.clear.mapper.BrandMapper接口中定义抽象方法,并使用注解来编写SQL语句,如下所示

但是这样肯定会出问题的,因为我们刚开始创建实体类Brand时,Brand中的属性与数据库表tb_brand中的字段名没有一一对应,这样mybatis无法帮我们完成自动封装,所以我们需要在BrandMapper映射文件中定义结果映射 ,使用resultMap标签。如下所示:

定义完结果映射关系后,在接口selectAll()方法上引用该结构映射。使用@ResultMap()注解来完成,如下所示:

接着我们来优化一下上面的配置:

我们发现,在BrandMapper映射文件中resultMap标签中的type属性需要填写Brand实体类的全限定名,这显然有点不简洁了,我们可以通过在mybatis核心配置文件中定义typeAliases标签来简化它,如下所示:

接着修改BrandMapper映射文件,如下所示

     

3.1.2、service方法的实现

在com.clear.service包下创建BrandService的接口,在该接口中定义查询所有的抽象方法

并在com.clear.service下再创建impl包;impl表示是放 service 层接口的实现类的包。 在该包下创建名为BrandServiceImpl类

这时就要疑惑了,之前我们都是直接定义service类,然后在里面定义方法,此处为什么要给 service 定义接口呢?因为service定义了接口后,在 servlet 中就可以使用多态的形式创建Service实现类的对象,如下:

这里使用多态是因为方便我们后期解除Servlet和service的耦合。从上面的代码我们可以看到SelectAllServlet类和BrandServiceImpl类之间是耦合在一起的,如果后期 BrandService 有其它更好的实现类(例如叫 BrandServiceImpl),那就需要修改SelectAllServlet类中的代码。后面我们学习了Spring框架后就可以解除SelectAllServlet类和红色框括起来的代码耦合。而现在咱们还做不到解除耦合,在这里只需要理解为什么定义接口即可。

BrandServiceImpl类代码如下:

package com.clear.service.impl;

import com.clear.mapper.BrandMapper;
import com.clear.pojo.Brand;
import com.clear.service.BrandService;
import com.clear.util.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;

public class BrandServiceImpl implements BrandService {
    //1. 创建SqlSessionFactory 工厂对象
    SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();

    /**
     * 查询所有
     * @return
     */
    @Override
    public List<Brand> selectAll() {
        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3. 获取BrandMapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4. 调用方法
        List<Brand> brands = brandMapper.selectAll();

        //5. 释放资源
        sqlSession.close();

        return brands;
    }
}

上述中用到了一个工厂对象,在com.clear.util包下创建SqlSessionFactoryUtils类,工厂对象的代码如下:

package com.clear.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

/**
 * 获取SqlSessionFactory对象的工具类
 */
public class SqlSessionFactoryUtils {
    private static SqlSessionFactory sqlSessionFactory;

    //静态代码块会随着类的加载而自动执行,且只执行一次
    static {
        InputStream inputStream = null;
        try {
            String resource = "mybatis-config.xml";
            inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getSqlSessionFactory(){
        return sqlSessionFactory;
    }
}

3.1.3、servlet实现

在 com.clear.web.servlet 包下定义名为SelectAllServlet的查询所有的servlet。该servlet逻辑如下:

1)调用service的selectAll()方法查询到所有的品牌数据,并返回结果

2)将返回的结果封装为json数据

3)响应json数据至前端

servlet代码如下:


@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 jsonString =JSON.toJSONString(brands);
        // 3、写数据
        response.setContentType("text/json;charset=utf-8"); //告知浏览器响应的数据类型, 告知浏览器使用什么字符集进行解码
        PrintWriter writer = response.getWriter();
        // 响应数据至浏览器
        writer.write(jsonString);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

3.1.4、测试后端程序

在浏览器输入访问 servlet 的资源路径 http://localhost:8080/brand-case/selectAllServlet ,如果没有报错,并能看到如下信息表明后端程序没有问题

3.2、前端实现

3.2.1、创建brand.html页面

在webapp目录下创建brand.html页面,如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

</body>
</html>

在页面引入Element 的css、js文件 和 Vue.js,如下:

一般是是在</body> 标签之前引入


<script src="js/vue.js"></script>
<script src="element-ui/lib/index.js"></script>
<link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css"> 

创建Vue核心对象,如下所示:

创建 Vue 对象时,需要传递一个 js 对象,而该对象中需要如下属性:

1)el: 用来指定哪些标签受 Vue 管理。 该属性取值 #app 中的 app 需要是受管理的标签的id属性值

2)data:用来定义数据模型

3)methods:用来定义函数。

定义div标签,它有一个id属性,值为app,如下:

3.2.2、官网复制Element组件代码

去Element官网复制组件代码,然后进行修改,步骤如下:

将html标签拷贝到 <div id="app"></div> 中,如下:

将css样式拷贝到我们页面的 head 标签中,如下

 将方法和模型数据拷贝到 Vue 对象指定的位置

 拷贝修改后的brand.html页面如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        /*表格的样式*/
        .el-table .warning-row {
            background: oldlace;
        }

        .el-table .success-row {
            background: #f0f9eb;
        }
    </style>
</head>
<body>
<div id="app">
    <template>
        <el-table
                :data="tableData"
                style="width: 100%"
                :row-class-name="tableRowClassName">
            <el-table-column
                    prop="brandName"
                    label="品牌名称"
                    align="center">
            </el-table-column>
            <el-table-column
                    prop="companyName"
                    label="企业名称"
                    align="center">
            </el-table-column>
            <el-table-column
                    prop="ordered"
                    label="排序"
                    align="center">
            </el-table-column>
            <el-table-column
                    prop="status"
                    label="当前状态"
                    align="center">
            </el-table-column>

        </el-table>
    </template>
</div>

<script src="js/vue.js"></script>
<script src="element-ui/lib/index.js"></script>
<link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css">

<script>
    new Vue({
        el: "#app",
        data() {
            return {
                tableData: [{
                    brandName: '2016-05-02',
                    companyName: '王小虎',
                    ordered: '上海市普陀区金沙江路 1518 弄',
                    status:"100"
                }]
            }
        },
        methods: {
            tableRowClassName({row, rowIndex}) {
                if (rowIndex === 1) {
                    return 'warning-row';
                } else if (rowIndex === 3) {
                    return 'success-row';
                }
                return '';

            }
        }
    })
</script>
</body>
</html>

其中,解释一下上述部分的含义:

 测试一下,上述brand.html页面代码,页面如下:

 接下来页面添加按钮,依然是去Element官网导入Element组件,如下所示:

 测试一下,上述brand.html页面代码,页面如下:

 接下来为每行的最前面添加复选框,依然是去Element官网复制Element组件,然后修改,如下所示:

找到这个带复选框的表格,去复制它的代码 

思考一下,当我们选择了复选框以后,应该要执行某种方法来传递 选择的这些行数据,所以我们观察Element组件代码后发现,在<el-table>标签中需要有一个事件 @selection-change="handleSelectionChange" ,如下

 还如要他绑定的函数,handleSelectionChange,如下所示:

从该函数中又发现还需要一个模型数据 multipleSelection ,所以还需要定义出该模型数据

标号列也用同样的方式进行拷贝并修改。

修改后的brand.html页面如下:

 

 测试结果如下所示:

接下来为每行最前面添加序号,依然是去Element官网复制Element组件,然后再修改,如下

找到前面带序号的表格,复制它的代码

 

修改后的brand.html代码如下:

 测试结果如下:

下面就可以开始完成查询所有的前端页面了

前端需要在页面加载完毕后发送 ajax 请求,所以发送请求的逻辑应该放在 mounted() 钩子函数中。而响应回来的数据需要赋值给表格绑定的数据模型,从下图可以看出表格绑定的数据模型是 tableData

前端代码如下

// 勾子函数,页面加载完毕后执行
        method(){
            //当页面加载完成后,发送异步请求,获取数据
            var _this = this;

            axios({
                method:"get",
                url:"http://localhost:8080/brand-case/selectAllServlet",
            }).then(function (resp) { // then回调函数
                _this.tableData = resp.data;
            });

启动服务器测试,如下

发现达到我们想要的效果,我们想要的效果是将数据库表tb_brand中所有记录都展示出来,但是显然没有这样效果,我们检查我们的前端代码,发现如下错误:

继续测试发现依然达不到想要的效果,经检查发现,我们的勾子函数写错了,如下:

 继续测试发现依然达不到想要的效果,经检查发现我们使用的是axios发送异步请求,但是我们并没有导入axios,下面进行导入

<script src="js/axios-0.18.0.js"></script>

 完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        /*表格的样式*/
        .el-table .warning-row {
            background: oldlace;
        }

        .el-table .success-row {
            background: #f0f9eb;
        }
    </style>
</head>
<body>
<div id="app">
    <template>
        <el-table
                :data="tableData"
                style="width: 100%"
                :row-class-name="tableRowClassName"
                @selection-change="handleSelectionChange">
            <el-table-column
                    type="selection"
                    width="55">
            </el-table-column>

            <el-table-column
                    type="index"
                    width="50">
            </el-table-column>

            <el-table-column
                    prop="brandName"
                    label="品牌名称"
                    align="center">
            </el-table-column>
            <el-table-column
                    prop="companyName"
                    label="企业名称"
                    align="center">
            </el-table-column>
            <el-table-column
                    prop="ordered"
                    label="排序"
                    align="center">
            </el-table-column>
            <el-table-column
                    prop="status"
                    label="当前状态"
                    align="center">
            </el-table-column>
            <el-table-column
                    align="center"
                    label="操作">
                <el-row>
                    <el-button type="primary">修改</el-button>
                    <el-button type="danger">删除</el-button>
                </el-row>
            </el-table-column>

        </el-table>
    </template>
</div>

<script src="js/vue.js"></script>
<script src="element-ui/lib/index.js"></script>
<link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css">

<script src="js/axios-0.18.0.js"></script>
<script>
    new Vue({
        el: "#app",
        data() {
            return {
                multipleSelection: [],

                // 表格数据
                tableData: []
            }
        },
        methods: {
            tableRowClassName({row, rowIndex}) {
                if (rowIndex === 1) {
                    return 'warning-row';
                } else if (rowIndex === 3) {
                    return 'success-row';
                }
                return '';

            },
            // 选择复选框后执行的函数
            handleSelectionChange(val) {
                this.multipleSelection = val;
            }
        },
        // 勾子函数,页面加载完毕后执行
        mounted(){
            //当页面加载完成后,发送异步请求,获取数据
            var _this = this;

            axios({
                method:"get",
                url:"http://localhost:8080/brand-case/selectAllServlet",
            }).then(function (resp) { // then回调函数
                _this.tableData = resp.data;
            });
        }
    })
</script>
</body>
</html>

测试结果如下:

 至此,查询所有功能的前后端页面均已完成

4、添加功能的实现

上图是添加数据的对话框,当点击 提交 按钮后就需要将数据提交到后端,并将数据保存到数据库中。下图是整体的流程:

页面发送请求时,需要将输入框输入的内容提交给后端程序,而这里是以 json 格式进行传递的。而具体的数据格式如下:

 注意:由于是添加数据,所以上述json数据中id是没有值的。

 4.1、后端的实现

4.1.1、dao层方法的实现

在BrandMapper接口中定义add()抽象的添加方法,并使用@Insert注解编写SQL语句,代码如下:

/**
     * 添加品牌
     * @param brand
     */
    @Insert("insert into tb_brand values(null, #{brandName}, #{companyName}, #{ordered}," +
            "#{description}, #{status})")
    void add(Brand brand);

4.1.2、serivce方法的实现

在BrandService接口中定义add(),添加品牌的业务逻辑方法 

    /**
     * 添加品牌
     * @param brand
     */
    void add(Brand brand);

在BrandServiceImpl类中重写add() 方法,并进行业务逻辑实现

    /**
     * 添加品牌
     * @param brand
     */
    public void add(Brand brand){
        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3. 获取BrandMapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4. 调用方法
        brandMapper.add(brand);
        // 提交事务
        sqlSession.commit();
        
        //5. 释放资源
        sqlSession.close();

    }

注意:增删改操作一定要提交事务

4.1.3、servlet的实现

在com.clear.web.servlet包写定义名为AddServlet的 Servlet。该 Servlet 的逻辑如下:

1)接收页面提交的数据。页面到时候提交的数据是 json 格式的数据,所以此处需要使用输入流读取数据

2)将接收到的数据转换为Brand对象

3)调用 service 的add方法进行添加的业务逻辑处理

4)给浏览器响应添加成功的标识,这里直接给浏览器响应 success 字符串表示成功


@WebServlet("/addServlet")
public class AddServlet extends HttpServlet {
    private BrandService brandService = new BrandServiceImpl();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1、接收品牌数据
        BufferedReader br = request.getReader();
        String jsonStr = br.readLine(); // 因为json字符串自有一行数据
        // 2、转为Brand对象
        Brand brand = JSON.parseObject(jsonStr, Brand.class);
        // 3、调用service完成添加
        brandService.add(brand);

        // 4、响应成功标识
        PrintWriter writer = response.getWriter();
        writer.write("success");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

4.2、前端实现

打开页面,我们并没有发现其有新增品牌的按钮,现在我们就将新增品牌的按钮做出来,因为待会还要批量删除的操作,因此这里一并把新增按钮、批量删除按钮都做出来

从 Element 官网找具有着色效果的按钮,并将代码拷贝到我们自己的页面上

 

修改后的brand.html如下

测试结果如下:

接下来为新增品牌按钮绑定一个对话框表单

去Element官网复制组件,如下

 修改的brand.html页面如下:

做完上述操作后,我们进行测试,结果如下:

我们发现,当点击新增品牌按钮是,确实可以弹出对话框,但是对话框里的内容不是一个表单,接下来我们将把对话框里的内容改变为一个表单

去Element官网找到类似的组件,复制粘贴,如下

 

将模型数据和方法复制去指定位置,如下所示

 测试如下:

 现在点击新增品牌按钮以后,可以弹出对话框表单,但他不是我们想要的,因此我们将继续修改它,如下:

测试,结果如下

 已经很接近我们要的效果了,观察发现,在备注下还缺少一个名为状态的滑块,还有右下角多了个两个按钮 取消与确定

现在为对话框表单添加一个滑块,去Element官网复制,如下

 

 

接着删除右下角的两个按钮

 测试如下

已经达到了我们想要的效果,但是我们点击提交或者是取消按钮没反应

思考一下,我们前面在创建对话框时为新增品牌按钮绑定了@click="dialogVisible = true"

 如下所示

在data()定义了如下模型数据

 

 意思就是说当 dialogVisible == false 时,对话框就会关闭,因此我们可以为取消按钮绑定一个单击事件 @click="dialogVisible = false",如下所示

接下来将提交按钮 绑定的单击事件改名,如下

接下来我们将修改对话框表中的绑定的模型数据

 修改模型数据

 修改后

  

 完成上述操作后,我们就可以在addBrand方法中编写代码来完成添加功能的前端了

上图左边是页面效果,里面的提交按钮可以通过上图右边看出绑定了一个 单击事件,而该事件绑定的是addBrand函数,所以添加品牌功能的逻辑代码应该写在addBrand()函数中。在此方法中需要发送异步请求并将表单中输入的数据作为参数进行传递。如下:

在then函数中的匿名函数是成功后的回调函数,而resp.data就可以获取到响应回来的数据,如果值是 success 表示数据添加成功。成功后我们需要做一下逻辑处理:

1、 关闭新增对话窗口

如下图所示是添加数据的对话框代码,从代码中可以看到此对话框绑定了 dialogVisible 数据模型,只需要将该数据模型的值设置为 false,就可以关闭新增对话框窗口了。

 这一步操作我们在前面已经做过了

2、重新查询数据

无论品牌数据添加成功与否,用户只要能在页面上查看到数据说明添加成功。而此处需要重新发送异步请求获取所有的品牌数据,而这段代码在查询所有功能中已经实现,所以我们可以将此功能代码进行抽取,抽取到一个selectAll() 函数中

接下来创建一个selectAll函数,将mounted()勾子函数中的代码复制过来,下面是勾子函数

selectAll函数如下:

3、弹出消息给用户提示添加成功

上图左边就是 elementUI 官网提供的成功提示代码,而上图右边是具体的效果。

注意:上面的this需要的是表示 VUE 对象的this   

综上所述,addBrand函数代码如下:

    // 添加品牌对话框表单点击提交按钮后执行的函数
            addBrand() {
                var _this = this;

                // 发送ajax请求,添加数据
                axios({
                    method: "post",
                    url: "http://localhost:8080/brand-case/addServlet",
                    data: _this.brand
                }).then(function (resp) {
                    //响应数据的处理逻辑
                    if (resp.data == "success") {
                        // 品牌添加成功,关闭对话框表单窗口
                        _this.dialogVisible = false;
                        // 查询查询数据
                        _this.selectAll();
                        // 弹出消息提示
                        _this.$message({
                            message: '恭喜你,品牌数据添加成功',
                            type: 'success'
                        });
                    }
                })

            },

完成了上面查询功能和添加品牌功能,接下来我们就来优化一下servlet目录下的代码

5、Servlet代码优化

5.1、问题引入

Web层中servlet的个数太多时,不便于管理和编写

将Servlet进行归类,对于同一个实体的操作方法,写到一个Servlet中。比如:BrandServlet、UserServlet,如下:

通过之前的两个功能,我们发现每一个功能都需要定义一个 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(...) {}
}

而我们知道发送请求 servlettomcat 会自动的调用 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 中就需要获取到资源的二级路径作为方法名,然后调用该方法,代码如下:


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);

        // 2、执行方法
        // 获取BrandServlet字节码对象 Class
        Class<? extends BaseServlet> cls = this.getClass();
        // 2.1、获取方法 Method对象
        try {
            Method method = cls.getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
            // 2.2、调用方法
            method.invoke(this,req,resp);

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

通过上面代码发现根据方法名获取对应方法的 Method 对象时需要指定方法参数的字节码对象。解决这个问题,可以将方法的参数类型规定死,而方法中可能需要用到 request 对象和 response 对象,所以指定方法的参数为 HttpServletRequestHttpServletResponse,那么 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) {}
}

5.2、代码优化

5.2.1、后端代码的优化

定义了 BaseServlet 后,针对品牌模块我们定义一个 BrandServlet 的 Servlet,并使其继承 BaseServlet 。在BrandServlet中定义 以下功能的方法:

1)查询功能:方法名声明为 selectAll ,并将之前的 SelectAllServlet 中的逻辑代码拷贝到该方法中

2)添加品牌功能:方法名声明为 add ,并将之前的 AddServlet 中的逻辑代码拷贝到该方法中

代码如下所示:


@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet{
    private BrandService brandService = new BrandServiceImpl();

    public void selectAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1、调用service查询
        List<Brand> brands = brandService.selectAll();
        System.out.println(brands);
        // 2、转为json
        String jsonString = JSON.toJSONString(brands);
        // 3、写数据
        response.setContentType("text/json;charset=utf-8"); //告知浏览器响应的数据类型, 告知浏览器使用什么字符集进行解码
        PrintWriter writer = response.getWriter();
        // 响应数据至浏览器
        writer.write(jsonString);
    }

    public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1、接收品牌数据
        BufferedReader br = request.getReader();
        String jsonStr = br.readLine(); // 因为json字符串自有一行数据
        // 2、转为Brand对象
        Brand brand = JSON.parseObject(jsonStr, Brand.class);
        // 3、调用service完成添加
        brandService.add(brand);

        // 4、响应成功标识
        PrintWriter writer = response.getWriter();
        writer.write("success");
    }
}

接着删除之前创建的两个Serlvet,如下

 5.2.2、前端代码的优化

页面中之前发送的请求的路径都需要进行修改,selectAll()函数中发送异步请求的 url 应该改为 http://localhost:8080/brand-case/brand/selectAll 。具体如下

addBrand()函数中发送异步请求的 url 应该改为 http://localhost:8080/brand-case/brand/add 。具体如下

 经测试,上面优化后的代码可以正常运行

6、批量删除功能实现

如上图所示点击选择多条数据前的复选框就意味着要删除这些数据,而点击了 批量删除 按钮后,需要让用户确认一下,因为有可能是用户误操作的,当用户确定后需要给后端发送请求并携带者需要删除数据的多个id值,后端程序删除数据库中的数据。具体的流程如下:

注意:  

        前端发送请求时需要将要删除的多个id值是以json格式提交给后端,而该json格式数据如下:

[1,2,3,4]

6.1、后端实现

6.1.1、dao层代码的实现

在BrandMapper接口中定义抽象方法deleteByIds(int[] ids),由于这里需要使用到动态SQL,属于比较复杂的SQL操作,因此我们不采用注解的方式实现,转而采用SQL映射文件配置

接口方法声明如下:

 /**
     * 批量删除
     * @param ids
     */
    void deleteByIds(@Param("ids") int[] ids);

在BrandMapper.xml映射配置文件中添加 statement,如下所示

6.1.2、service方法实现

在BrandService接口中定义抽象方法deleteByIds(int[] ids),批量删除的业务逻辑方法


    /**
     * 批量删除
     * @param ids
     */
    void deleteByIds(int[] ids);

BrandServiceImpl类中重写deleteByIds(int[] ids)方法,并进行业务逻辑实现,代码如下:

 /**
     * 批量删除
     * @param ids
     */
    public void deleteByIds(int[] ids){
        // 2、获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3、获取BrandMapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        // 4、调用方法
        brandMapper.deleteByIds(ids);
        // 提交事务
        sqlSession.commit();
        
        // 5、释放资源
        sqlSession.close();
        
    }

6.1.2、servlet的实现

因为我们在前面优化过servlet,因此我们创建的servlet不再继承HttpServlet,在前面我们定义了BrandServlet,继承自BaseServlet,而BaseServlet继承自HttpServlet,相当于我们间接继承了HttpServlet

现在我们在BrandServlet中定义deleteByIds()这个方法,该方法代码逻辑如下:

1)接收页面提交的数据。页面到时候提交的数据是 json 格式的数据,所以此处需要使用输入流读取数据

2)将接收到的数据转换为 int[] 数组

3)调用 service 的 deleteByIds() 方法进行批量删除的业务逻辑处理

4)给浏览器响应添加成功的标识,这里直接给浏览器响应 success 字符串表示成功

代码如下:

/**
     * 批量删除
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void deleteByIds(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1、接收数据(json串)
        BufferedReader br = request.getReader();
        String jsonStr = br.readLine();

        // 2、将接收到的数据转为数组
        int[] ids = JSON.parseObject(jsonStr, int[].class);

        // 3、调用service完成批量删除
        brandService.deleteByIds(ids);
        // 4、响应成功标识
        PrintWriter writer = response.getWriter();
        writer.write("success");

    }

6.2、前端实现

此功能的前端代码实现稍微有点麻烦,分为以下几步实现

6.2.1、获取选择的id值

我们前面做页面表格的时候代码如下:

 

从上图可以看出表格复选框绑定了一个 selection-change 事件,该事件是当选择项发生变化时会触发。该事件绑定了 handleSelectionChange 函数,而该函数有一个参数 val ,该参数是获取选中行的数据,如下:

 

而我们只需要将所有选中数据的id值提交给服务端即可,获取id的逻辑我们书写在批量删除 按钮绑定的函数中。

批量删除 按钮绑定单击事件,并给绑定触发时调用的函数,如下

并在Vue对象中的 methods 中定义 deleteByIds() 函数,在该函数中从 multipleSelection 数据模型中获取所选数据的id值。要完成这个功能需要在 Vue 对象中定义一个数据模型 selectedIds:[],在 deleteByIds() 函数中遍历 multipleSelection 数组,并获取到每一个所选数据的id值存储到 selectedIds 数组中,代码实现如下:

6.2.2、发送异步请求

使用 axios 发送异步请求并经上一步获取到的存储所有的 id 数组作为请求参数

 6.2.3、确认框提示

由于删除操作是比较危险的;有时候可能是由于用户的误操作点击了 批量删除 按钮,所以在点击了按钮后需要先给用户确认提示。而确认框在 elementUI 中也提供了,如下图

而在点击 确定 按钮后需要执行之前删除的逻辑。将前面两步操作整合在一起,确保用户点击了确定按钮在执行,优化后前端代码实现如下:

   // 批量删除按钮绑定的函数
        deleteByIds() {
            // 弹出提示框,询问用户是否确认删除
            this.$confirm("该操作将永久删除这些数据,是否继续?", "警告", {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                // 用户点击确定后的逻辑

                // 1、遍历从 multipleSelection 中 获取id
                for (let i = 0; i < this.multipleSelection.length; i++) {
                    let selectElement = this.multipleSelection[i];
                    this.selectedIds[i] = selectElement.id;  // 将id存入数组
                }
                // 2、发送Ajax请求
                var _this = this;
                axios({
                    method: "post",  // 增删改都用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:'已取消删除'
                });
            });
        },

测试批量删除功能

启动服务器,选择复选框,点击批量删除按钮

 删除成功

7、分页查询功能的实现

7.1、分析

7.1.1、分页查询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;

以此类推

7.1.2、前后端数据的分析

分页查询功能时候比较复杂的,所以我们要先分析清楚以下两个问题:

前端需要传递什么参数给后端

根据上一步对分页查询 SQL 语句分析得出,前端需要给后端两个参数

1)当前页码 : currentPage

2)每页显示条数:pageSize

后端需要响应什么数据给前端

上图是分页查询页面展示的效果,从上面我们可以看出需要响应以下相关联数据

1)当前页需要展示的数据。我们在后端一般会存储到 List 集合中

2)总共记录数。在上图页面中需要展示总的记录数,所以这部分数据也需要。总的页面 elementUI 的分页组件会自动计算,我们不需要关心

而这两部分需要封装到 PageBean 对象中,并将该对象转换为 json 格式的数据响应回给浏览器

通过上面的分析我们需要先在 pojo 包下创建 PageBean 类,为了做到通过会将其定义成泛型类,代码如下:

package com.clear.pojo;

import java.util.List;

/**
 * 分页查询JavaBean
 */
public class PageBean<T> {

    private Integer totalCount;  // 总记录数
    private List<T> rows;  // 当前页数据

    public Integer getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(Integer totalCount) {
        this.totalCount = totalCount;
    }

    public List<T> getRows() {
        return rows;
    }

    public void setRows(List<T> rows) {
        this.rows = rows;
    }
}

7.1.3、流程分析

后端需要响应总记录数和当前页的数据 两部分数据给前端。所以在BrandMapper接口中需要定义两个方法:

        selectByPage():查询当前页的数据的方法

        selectTotalCount():查询总记录的方法

整体流程如下:

7.2、后端实现 

7.2.1、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();

7.2.2、service方法实现

在BrandService接口中定义selectBypage() 分页查询数据的业务逻辑方法

    /**
     * 分页查询
     * @param currentPage  当前页码
     * @param pageSize  每页展示条数
     * @return
     */
    PageBean<Brand> selectByPage(int currentPage, int pageSize);
 

在BrandServiceImpl 类中重写 selectByPage() 方法,并进行业务逻辑实现

@Override
    public PageBean<Brand> selectByPage(int currentPage, int pageSize) {
        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3. 获取BrandMapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4. 计算开始索引
        int begin = (currentPage - 1) * pageSize;
        // 计算查询条目数
        int size = pageSize;
        //5. 查询当前页数据
        List<Brand> brands = brandMapper.selectByPage(currentPage, pageSize);
        //6. 查询总记录数
        int totalCount = brandMapper.selectTotalCount();

        //7. 封装PageBean对象
        PageBean<Brand> pageBean = new PageBean<>();
        pageBean.setRows(brands);
        pageBean.setTotalCount(totalCount);
        //8. 释放资源
        sqlSession.commit();

        return pageBean;
    }

7.2.3、servlet实现

在BrandSerlet类中定义selectByPage()方法。该方法的逻辑如下:

1)获取页面提交的 当前页码每页显示条目数 两个数据。这两个参数是在url后进行拼接的,格式是 url?currentPage=1&pageSize=5。获取这样的参数需要使用 requet.getparameter() 方法获取。

2)调用 service 的 selectByPage() 方法进行分页查询的业务逻辑处理

3)将查询到的数据转换为 json 格式的数据

4)响应 json 数据至前端

BrandServlet中selectByPage()方法代码实现如下:


    /**
     * 分页查询功能
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void selectByPage(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);

        //2. 调用service查询
        PageBean<Brand> pageBean = brandService.selectByPage(currentPage, pageSize);

        //2. 转为JSON
        String jsonString = JSON.toJSONString(pageBean);
        //3. 写数据
        response.setContentType("text/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(jsonString);
    }

7.2.4、测试

在浏览器上地址栏输入 http://localhost:8080/brand-case/brand/selectByPage?currentPage=1&pageSize=5 ,查询到以下数据

7.3、前端实现

7.3.1、selectAll()的改进

selectAll()函数之前是查询所有数据,现需要改成分页查询。 请求路径应改为 http://localhost:8080/brand-case/brand/selectByPage?currentPage=1&pageSize=5 ,而 currentPagepageSize 是需要携带的参数,分别是 当前页码 和 每页显示的条目数。

刚才我们对后端代码进行测试可以看出响应回来的数据,所以在异步请求的成功回调函数(then 中的匿名函数)中给页面表格的数据模型赋值 _this.tableData = resp.data.rows;。整体代码如下

    var _this = this;

                axios({
                    method: "get",
                    url: "http://localhost:8080/brand-case/brand/selectByPage?currentPage=1&pageSize=5",
                }).then(function (resp) {
                    // 设置表格数据
                    _this.tableData = resp.data.rows;  // {rows:[],totalCount:100}
                });

响应的数据中还有总记录数,要进行总记录数展示需要在页面绑定数据模型

而页面中分页组件给 当前页码每页显示的条目数 都绑定了数据模型

所以 selectAll() 函数中发送异步请求的资源路径中不能将当前页码和 每页显示条目数写死,代码就可以优化为

 7.3.2、改变每页条目数

        

当我们改变每页显示的条目数后,需要重新发送异步请求。而下图是分页组件代码,@size-change 就是每页显示的条目数发生变化时会触发的事件

 而该事件绑定了一个 handleSizeChange 函数,整个逻辑如下:

7.3.3、 改变当前页码

当我们改变页码时,需要重新发送异步请求。而下图是分页组件代码,@current-change 就是页码发生变化时会触发的事件

 而该事件绑定了一个 handleCurrentChange 函数,整个逻辑如下

 测试

经测试,页面上啥也没有,我们检查代码,检查后发现分页工具条中的模型数据没有在Vue对象中声明出来,如下所示:

再次测试,分页工具条就可以使用了,如下

 8、条件查询功能实现

 上图就是用来输入条件查询的条件数据的。要做条件查询功能,先明确以下三个问题

1)三个条件之间满足什么关系?

同时满足,所用 SQL 中多个条件需要使用 and 关键字连接

2)3个条件必须全部填写吗?

不需要。想根据哪儿个条件查询就写那个,所以这里需要使用动态 sql 语句

3)条件查询需要分页吗?

需要

整个条件分页查询流程如下:

8.1、后端实现

8.1.1、dao层实现

在BrandMapper接口中定义selectByPageAndCondition() 方法 和selectTotalCountByCondtion()方法 ,用来进行条件分页查询功能,方法如下:

/**
     * 分页条件查询
     *
     * @param begin
     * @param size
     * @param brand
     * @return
     */
    List<Brand> selectByPageAndCondition(@Param("begin") int begin,
                                         @Param("size") int size, @Param("brand") Brand brand);


    /**
     * 根据条件查询总记录数
     *
     * @return
     */
    int selectTotalCountByCondition(Brand brand);

参数:

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>

8.1.2、service的实现

BrandService 接口中定义 selectByPageAndCondition() 分页查询数据的业务逻辑方法

/**
     * 分页条件查询
     * @param currentPage
     * @param pageSize
     * @param brand
     * @return
     */
    PageBean<Brand> selectByPageAndCondition(int currentPage, int pageSize, Brand brand);

BrandServiceImpl 类中重写 selectByPageAndCondition() 方法,并进行业务逻辑实现


    /**
     * 分页条件查询
     * @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);

        //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 = brandMapper.selectByPageAndCondition(begin, size, brand);

        //6. 查询总记录数
        int totalCount = brandMapper.selectTotalCountByCondition(brand);

        //7. 封装PageBean对象
        PageBean<Brand> pageBean = new PageBean<>();
        pageBean.setRows(rows);
        pageBean.setTotalCount(totalCount);

        //8. 释放资源
        sqlSession.close();

        return pageBean;
    }

8.1.3、servlet的实现

BrandServlet 类中定义 selectByPageAndCondition() 方法。而该方法的逻辑如下:

1)获取页面提交的 当前页码每页显示条目数 两个数据。这两个参数是在url后进行拼接的,格式是 url?currentPage=1&pageSize=5。获取这样的参数需要使用 requet.getparameter() 方法获取。

2)获取页面提交的 条件数据 ,并将数据封装到一个Brand对象中。由于这部分数据到时候是需要以 json 格式进行提交的,所以我们需要通过流获取数据,具体代码如下:

// 获取查询条件对象
BufferedReader br = request.getReader();
String params = br.readLine();//json字符串

//转为 Brand
Brand brand = JSON.parseObject(params, Brand.class);

3)调用 service 的 selectByPageAndCondition() 方法进行分页查询的业务逻辑处理

4)将查询到的数据转换为 json 格式的数据

5)响应 json 数据

Brandservlet 中 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();

        // 转为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");
        PrintWriter writer = response.getWriter();
        writer.write(jsonString);
    }

8.2、前端实现

前端代码我们从以下几方面实现:

1、查询表单绑定查询条件对象模型

如下所示:

2、 点击查询按钮查询数据

从上面页面可以看到给 查询 按钮绑定了 query() 函数,而在query() 函数中只需要调用 selectAll() 函数进行条件分页查询,如下:

3、改进 selectAll() 函数

子页面加载完成后发送异步请求,需要携带当前页码、每页显示条数、查询条件对象。接下来先对携带的数据进行说明:

1)当前页码每页显示条数 这两个参数我们会拼接到 URL 的后面

2)查询条件对象 这个参数需要以 json 格式提交给后端程序

修改 selectAll() 函数逻辑为

 9、前端代码优化

咱们已经将所有的功能实现完毕。而针对前端代码中的发送异步请求的代码,如下

需要在成功的回调函数(也就是then 函数中的匿名函数)中使用this,都需要在外边使用 _this 记录一下 this 所指向的对象;因为在外边的 this 表示的是 Vue 对象,而回调函数中的 this 表示的不是 vue 对象。这里我们可以使用 ECMAScript6 中的新语法(箭头函数)来简化这部分代码,如上面的代码可以简化为:

 箭头函数语法 :

(参数) => {
    逻辑代码
}

箭头函数的作用:

替换(简化)匿名函数。

测试

启动服务器测试发现,如下问题

当前状态这列并不是我们想要的 启用或禁用

现在我们优化一下代码,先去Brand实体类中进行如下修改 

 

 修改brand.html页面

修改后测试,效果如下:

 10、修改功能的实现

当用户点击修改按钮以后,会弹出对话框表单(表单中有回显数据),当点击 提交 按钮后就需要将数据提交到后端,并将数据保存到数据库中。

10.1、后端实现

10.1.1、dao层方法的实现

在BrandMapper接口中定义一个update()抽象方法,并使用@Update注解编写SQL语句

 /**
     * 修改品牌
     * @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);

10.1.2、service方法的实现

BrandService 接口中定义 update() 添加数据的业务逻辑方法

    /**
     * 修改品牌
     * @param brand
     */
    void update(Brand brand);

BrandServiceImpl 类中重写 update() 方法,并进行业务逻辑实现

/**
     * 添加品牌
     *
     * @param brand
     */
    public void add(Brand brand) {
        //2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3. 获取BrandMapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4. 调用方法
        brandMapper.add(brand);
        // 提交事务
        sqlSession.commit();

        //5. 释放资源
        sqlSession.close();

    }

10.1.3、servlet实现

在BrandServlet定义update()方法,代码如下:


    /**
     * 修改品牌
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1、接收品牌数据
        BufferedReader br = request.getReader();
        String jsonStr = br.readLine(); // 因为json字符串自有一行数据
        // 2、转为Brand对象
        Brand brand = JSON.parseObject(jsonStr, Brand.class);
        // 3、调用service完成添加
        brandService.update(brand);

        // 4、响应成功标识
        PrintWriter writer = response.getWriter();
        writer.write("success");
    }

10.2、前端实现

当点击修改按钮时,弹出对话框表单进行修改

11、删除功能的实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/78777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【人工智能与机器学习】——朴素贝叶斯与支持向量机(学习笔记)

&#x1f4d6; 前言&#xff1a;朴素贝叶斯&#xff08;Naive Bayes&#xff09;和支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是两种不同的机器学习算法&#xff0c;它们都用于分类。朴素贝叶斯算法基于贝叶斯定理来进行分类&#xff0c;它是一…

内存优化之掌握 APP 运行时的内存模型

在上一章&#xff0c;我们已经从操作系统的维度了解了一个进程的内存模型。这一节&#xff0c;我们将维度继续上升&#xff0c;从应用层出发看看一个 App 运行时的内存模型是怎样的。从 App 运行时的内存模型中我们可以知道导致内存增长的源头&#xff0c;从源头出发&#xff0…

【RCNN系列】Faster RCNN论文总结及源码

目标检测论文总结 【RCNN系列】 RCNN Fast RCNN Faster RCNN 文章目录目标检测论文总结前言一、Pipeline二、模型设计1.RPNHead2.Anchors3.RPN&#xff08;Region Proposal Networks&#xff09;4.RPN正负样本划分阈值5.训练策略三、总结前言 一些经典论文的总结。 一、Pipel…

RK3568平台开发系列讲解(驱动基础篇)Linux内核面向对象思想之继承

🚀返回专栏总目录 文章目录 一、私有指针二、抽象类三、接口沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在面向对象编程中,封装和继承其实是不分开的:封装就是为了更好地继承。我们将几个类共同的一些属性和方法抽取出来,封装成一个类,就是为了通过继承最大化…

【云原生】devops之jenkins中pipeline语法(2)

前言&#xff1a; pipeline语法分类一般来说&#xff0c;有四种。分别是环境配置、阶段步骤、行为动作、逻辑判断。 二、阶段步骤 &#xff08;1&#xff09;post 根据pipeline块或者stage块&#xff08;阶段&#xff09;完成的状态来进行一个或者多个附加步骤&#xff08;取决…

搭建高性能数据库服务⭐《Sharding-JDBC+Canal》⭐

本文主要记录本周的学习内容&#xff0c;搭建mysql的高性能数据库服务 源于 现最多被使用的数据库还是Msql&#xff0c;而MySQL本身不是一种分布式型数据库&#xff0c;在高性能要求下&#xff0c;简单的主从、复制已无法满足高性能要求。 而本文主要在提供读者一种高性能方案…

Java学习之equals方法练习

目录 第一题 题目要求 我的代码 创建Person类 main类 结果 重写equals 重写后的结果 老师代码 思路 结果 总结 Interger类 源代码 String类 源代码 第二题 运行结果 第三题 知识点 运行结果 第一题 题目要求 判断两个 Person 对象的内容是否相等&#xff0c;…

SQL开窗函数之基本用法和聚合函数

开窗函数 当我们需要进行一些比较复杂的子查询时&#xff0c;聚合函数就会非常的麻烦&#xff0c;因此可以使用开窗函数进行分组再运用函数查询。窗口函数既可以显示聚集前的数据&#xff0c;也可以显示聚集后的数据&#xff0c;可以在同一行中返回基础行的列值和聚合后的结果…

python之模块使用

目录 一、模块 二、标准模块 collections模块 三、异常处理 四、文件操作 一、模块 Python 模块(Module)&#xff0c;是一个 Python 文件&#xff0c;以 .py 结尾&#xff0c;包含了 Python 对象定义和Python语句。 模块让你能够有逻辑地组织你的 Python 代码段。 新建util…

day17【代码随想录】找出字符串中第一个匹配项的下标 、重复的子字符串

文章目录前言一、找出字符串中第一个匹配项的下标&#xff08;力扣28&#xff09;二、重复的子字符串&#xff08;力扣459&#xff09;前言 1、找出字符串中第一个匹配项的下标 2、重复的子字符串 一、找出字符串中第一个匹配项的下标&#xff08;力扣28&#xff09; 给你两个…

LeetCode HOT 100 —— 200 .岛屿问题

题目 给你一个由 ‘1’&#xff08;陆地&#xff09;和 ‘0’&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以假设该…

有意思,圣诞节自己做一个装饰圣诞帽头像的APP!

话说又到了一年一度到别人到节日&#xff0c;圣诞节&#xff0c;还记得去年的时候&#xff0c;朋友圈疯狂转发到圣诞帽嘛&#xff0c;在圣诞节为自己到头像增加一款圣诞帽还是蛮应景的。 我们的目标就是是这样的 ❝ 当然&#xff0c;如果你对过程不感兴趣&#xff0c;那么直接到…

总结本人学习b站黑马前端课程,各部分案例汇总

目录 1.Ajax&#xff1a; 对应课程&#xff1a; b站黑马JavaScript的Ajax案例代码——新闻列表案例 b站黑马JavaScript的Ajax案例代码——评论列表案例 b站黑马JavaScript的Ajax案例代码——聊天机器人案例 b站黑马JavaScript的Ajax案例代码——图书管理案例 2.ES6面向对…

HTML-如何让网站变成灰色?

在某些特定的时候&#xff0c;我们经常会看到网站会将整体布局设置成灰色色调&#xff0c;以示哀悼。 那么这是怎么实现的呢&#xff1f; 我去查了下相关的文章&#xff0c;发现是通过CSS的 过滤器函数 实现的&#xff0c;详见&#xff1a;grayscale()。 grayscale:对图片进…

MySql性能优化(三)执行计划详解

执行计划 执行计划执行计划概述idselect_typetabletypepossible_keyskeykey_lenrefrowsextra官网地址 在具体的应用当中&#xff0c;我们排查sql有没有走索引&#xff0c;性能如何&#xff0c;需要查看Sql语句具体的执行过程&#xff0c;以方便我们调整sql来加快sql的执行效率。…

如何通过 IntelliJ IDEA 来提升 Java8 Stream 的编码效率

小新再次推荐一篇 &#xff0c;主要是讲如何通过 IntelliJ IDEA 来提升 Stream 的编码效率&#xff0c;算是一个小技巧&#xff0c;经常使用 Java8 Stream 流的小伙伴们&#xff0c;可以试下&#xff0c;能够提升工作效率哦&#xff01; 一、Java8 Stream API 的不友好性 Java…

02优先队列和索引优先队列-优先队列-数据结构和算法(Java)

文章目录1 概述1.1 需求1.2 优先队列特点1.3 优先队列分类1.4 应用场景1.5 相关延伸2 说明3 索引优先队列3.1 实现思路3.2 API设计3.2 代码实现及简单测试5 主要方法讲解5.1 exch()5.2 insert()5.2 poll()6 分析7 后记1 概述 普通的队列是一种先进先出的数据结构&#xff0c;元…

JMeter连接Oracle过程及常见问题总结

如果被测试系统使用的数据库不是MySQL而是Oracle&#xff0c;如何用JMeter进行连接呢&#xff1f; 一、下载数据库驱动 需要确认数据库的版本&#xff0c;可以上网站下载驱动&#xff1a;https://www.oracle.com/database/technologies/jdbc-ucp-122-downloads.html。 或者直…

【Linux】进程间通信-共享内存

前言 我们知道&#xff0c;在Linux中&#xff0c;进程是相互独立存在的&#xff0c;不存在直接让进程之间互相通信的方式。但是如果我们能让不同进程之间见到同一块内存&#xff0c;也就是都能读写这片区域是不是就能够达到进程间通信呢&#xff1f; 事实证明确实如此。在之前我…

【springboot进阶】基于starter项目构建(二)构建starter项目-fastjson

目录 一、创建 fastjson-spring-boot-starter 项目 二、添加 pom 文件依赖 三、构建配置 四、加载自动化配置 五、打包 六、使用 这个系列讲解项目的构建方式&#xff0c;主要使用 父项目 parent 和 自定义 starter 结合。项目使用最新的 springboot3 和 jdk19。本系列的…