JavaWeb综合案例

学习目标

  • 查询所有
  • 新增品牌
  • 修改品牌
  • 删除品牌
  • 批量删除
  • 分页查询
  • 条件查询

学习内容

  • 查询所有
  • 新增品牌
  • 修改品牌
  • 删除品牌
  • 批量删除
  • 分页查询
  • 条件查询

学习产出

综合案例资料

1.查询所有功能

1.1 环境准备

  • 将资料中的brand-case导入进去,将环境配置好,并将所需要改的链接数据库的账号密码修改成自己的。
  • 执行sql数据库文件

分析
要实现这个功能,要先搞明白如下问题:

  • 什么时候发送异步请求?

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

  • 请求需要携带参数吗?

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

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

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

1.2 后端实现

BranadMapper

com.zpd,mapper接口中定义抽象方法,并使用 @Select 注解编写 sql 语句。

/**
     * 查询所有
     * @return
     */
@Select("select * from tb_brand")
List<Brand> selectAll();

由于表中有些字段名和实体类中的属性名没有对应,所有需要在resources.mybatis-config.xml 映射配置文件中定义结果映射,使用result标签。

<?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 property="brandName" column="brand_name" />
        <result property="companyName" column="company_name" />
    </resultMap>
</mapper>

定义完后,在BrandMapper中写ResultMap注解。

/**
     * 查询所有
     * @return
     */
@Select("select * from tb_brand")
@ResultMap("brandResultMap")
List<Brand> selectAll();

BrandService

在该接口中定义查询所有的抽象方法

public interface BrandService {

    /**
     * 查询所有
     * @return
     */
    List<Brand> selectAll();
}

并在service创建impl包,并创建impl文件实现BrandService接口。

import java.util.List;

/**
 * @Description
 * @Author zpd
 * @Date 2022/6/21
 */
public class BrandServiceImpl implements BrandService {
    //1、创建SqlSessionFactory
    SqlSessionFactory factory =SqlSessionFactoryUtils.getSqlSessionFactory();
    @Override
    public List<Brand> selectAll() {
        //2、获取sqlSession对象
        SqlSession sqlSession = factory.openSession();
        //3、获取BrandMapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4、调用方法
        List<Brand> brands = brandMapper.selectAll();
        //5、释放资源
        sqlSession.close();
        return brands;
    }

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

Servlet
servlet 逻辑如下:

  • 调用service的 selectAll() 方法查询所有的品牌数据,并接口返回结果
  • 将返回的结果转换为 json 数据
  • 响应 json 数据

在web下创建SelectAllServlet
代码如下

package com.zpd.web; /**
 * @Description
 * @Author zpd
 * @Date 2022/6/21
 */

import com.alibaba.fastjson.JSON;
import com.zpd.pojo.Brand;
import com.zpd.service.BrandService;
import com.zpd.service.impl.BrandServiceImpl;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
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 jsonString = JSON.toJSONString(brands);
        //3、写数据
        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);
    }
}

打开tomcat,在浏览器上访问,http://localhost:8080/brand-case/selectAllServlet,当显示的是数据库的数据时说明后端代码正确。
显示

1.2 前端实现

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

 mounted(){
     //当页面加载完成后,发送异步请求,获取数据
     var _this = this;

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

2 添加数据

显示
上图是添加数据的对话框,当点击 提交 按钮后就需要将数据提交到后端,并将数据保存到数据库中。下图是整体的流程:
显示
页面发送请求时,需要将输入框输入的内容提交给后端程序,而这里是以 json 格式进行传递的。

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

2.1 后端实现

按照以前的方法,在BrandMapper中添加add方法,添加的数据类型是Brand类型。在BrandService接口中定义添加的方法,并在impl中实现add方法。在web中通过addServlet添加数据。
BrandMapper代码

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

BrandMapper代码

    /**
     * 添加数据
     * @param brand
     */
    void add(Brand brand);

BrandMapperImpl代码

   @Override
    public void add(Brand brand) {
        SqlSession sqlSession = factory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        mapper.add(brand);
        sqlSession.commit();
        sqlSession.close();
    }

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

该 Servlet 的逻辑如下:

  • 接收页面提交的数据。页面到时候提交的数据是 json 格式的数据,所以此处需要使用输入流读取数据
  • 将接收到的数据转换为 Brand 对象
  • 调用 service 的 add() 方法进行添加的业务逻辑处理
  • 给浏览器响应添加成功的标识,这里直接给浏览器响应 success 字符串表示成功
    servlet 代码实现如下:
@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 params = br.readLine();//json字符串
        //转为Brand对象
        Brand brand = JSON.parseObject(params, Brand.class);
        //2. 调用service添加
        brandService.add(brand);
        //3. 响应成功的标识
        response.getWriter().write("success");
    }

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

2.2 前端实现

我们可以看出代码部分,在提交的标签中有@click方法,绑定了一个单击函数,当点击提交时就会调用addBrand函数,所以添加的方法写在addBrand()函数中。

            addBrand(){
                //console.log(this.brand);

                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();
                    }
                    //alert(resp.data);
                })
            }

then 函数中的匿名函数是成功后的回调函数,而 resp.data 就可以获取到响应回来的数据,如果值是 success 表示数据添加成功。在数据获取成功后需要进行以下两步操作:

  1. 关闭新增对话框窗口
    从代码中可以看到此对话框绑定了 dialogVisible 数据模型,只需要将该数据模型的值设置为 false,就可以关闭新增对话框窗口了。
  2. 重新查询数据
    数据添加成功与否,用户只要能在页面上查看到数据说明添加成功。而此处需要重新发送异步请求获取所有的品牌数据,而这段代码在 查询所有 功能中已经实现,所以我们可以将此功能代码进行抽取,抽取到一个 selectAll() 函数中。
   // 查询所有数据
   selectAll(){
       var _this = this;
   
       axios({
           method:"get",
           url:"http://localhost:8080/brand-case/selectAllServlet"
       }).then(function (resp) {
           _this.tableData = resp.data;
       })
   }

那么就需要将 mounted() 钩子函数中代码改进为

   mounted(){
       //当页面加载完成后,发送异步请求,获取数据
       this.selectAll();
   }

同时在新增响应的回调中调用 selectAll() 进行数据的重新查询。
3. 弹出消息给用户提示添加成功
显示
这是Element官网提供的代码

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

综上所述,前端代码如下:

// 添加数据
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'
            });
        }
    })
}

那么就需要将 mounted() 钩子函数中代码改进为

   mounted(){
       //当页面加载完成后,发送异步请求,获取数据
       this.selectAll();
   }

3 代码优化

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(...) {}
}

而我们知道发送请求 servlettomcat 会自动的调用 service() 方法,之前我们在自定义的 servlet 中重写 doGet() 方法和 doPost() 方法,当我们访问该 servlet 时会根据请求方式将请求分发给 doGet() 或者 doPost() 方法。

为了做到通用,我们定义一个通用的 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(...) {} 
    
    //添加brand信息
    public void add(...) {}
    
    //修改brand信息
    public void update(...) {}
    
    //删除brand信息
    public void delete(...) {}
}

可以规定在发送请求时,请求资源的二级路径(/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 中就需要获取到资源的二级路径作为方法名,然后调用该方法。

package com.zpd.web;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *
 * @Description
 * 替换HttpServlet,根据请求的最后一段路径来进行方法分发。
 * @Author zpd
 * @Date 2022/6/23
 */
public class BaseServlet extends HttpServlet {
    //根据请求的最后一段路径进行方法分发。
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的路径
        String uri = request.getRequestURI();//brand-case/brand/selectAll
        //System.out.println(uri);
        //获取最后一段路径方法名
        int i = uri.lastIndexOf("/");
        String methodName = uri.substring(i+1);//最后一段路径方法的名称
        //System.out.println(methodName);
        //2、执行方法
        //2.1、获取BrandServlet  /UserServlet  字节码对象Class
        /*谁调用我(this 所在的方法Service),我(this)代表谁
        System.out.println(this);//代表谁  BrandServlet? HttpServlet?  BrandServlet*/
        //2.2、获取方法  Method对象
        Class<? extends BaseServlet> aClass = this.getClass();
        try {
            Method method = aClass.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            //2.3、执行方法
            method.invoke(this,request,response);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

通过上面代码发现根据方法名获取对应方法的 Method 对象时需要指定方法参数的字节码对象。解决这个问题,可以将方法的参数类型规定死,而方法中可能需要用到 request 对象和 response 对象,所以指定方法的参数为 HttpServletRequestHttpServletResponse,那么 BrandServlet 代码就可以改进为:

@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {
    //实现查询
	public void selectAll(HttpServletRequest req, HttpServletResponse resp) {}
    
    //添加brand信息
    public void add(HttpServletRequest req, HttpServletResponse resp) {}
    
}

后端优化

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

  • 查询所有 功能:方法名声明为 selectAll ,并将之前的 SelectAllServlet 中的逻辑代码拷贝到该方法中
  • 添加数据 功能:方法名声明为 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();

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

    public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1. 接收品牌数据
        BufferedReader br = request.getReader();
        String params = br.readLine();//json字符串

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

        //2. 调用service添加
        brandService.add(brand);

        //3. 响应成功的标识
        response.getWriter().write("success");
    }
}

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

// 查询分页数据
selectAll(){
    var _this = this;

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

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

// 添加数据
addBrand() {
    //console.log(this.brand);
    var _this = this;

    // 发送ajax请求,添加数据
    axios({
        method:"post",
        url:"http://localhost:8080/brand-case/brand/add",
        data:_this.brand
    }).then(function (resp) {
        if(resp.data == "success"){
            //添加成功
            //关闭窗口
            _this.dialogVisible = false;
            // 重新查询数据
            _this.selectAll();
            // 弹出消息提示
            _this.$message({
                message: '恭喜你,添加成功',
                type: 'success'
            });
        }
    })
}

4 批量删除

显示
具体的流程如图,当选中多个复选框,点击删除的时可以进行一次判断,防止用户误删,当个用户确定后,将多个id值发送给后端,并请求删除多跟id对应的数据。
注意:

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

[1,2,3,4]

4.1 dao方法实现

BrandMapper中,编写deleteByIds方法,因为要用到多个id,所以建议使用映射文件。

BrandMapper.java

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

BrandMapper.xml

    <delete id="deleteByIds">
       delete from tb_brand where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>

4.2 service方法实现

BrandService 接口中定义 deleteByIds() 批量删除的业务逻辑方法

void deleteByIds(@Param("ids") int[] ids);

并在 BrandServiceImpl中重写deleteByIds() 方法。

    @Override
    public void deleteByIds(int[] ids) {
        SqlSession sqlSession = factory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        mapper.deleteByIds(ids);
        sqlSession.commit();
        sqlSession.close();
    }

4.3 servlet实现

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

  • 接收页面提交的数据。页面到时候提交的数据是 json 格式的数据,所以此处需要使用输入流读取数据
  • 接收页面提交的数据。页面到时候提交的数据是 json 格式的数据,所以此处需要使用输入流读取数据
  • 将接收到的数据转换为 int[] 数组
  • 调用 service 的 deleteByIds() 方法进行批量删除的业务逻辑处理
  • 给浏览器响应添加成功的标识,这里直接给浏览器响应 success 字符串表示成功
    /**
     * 批量删除
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void deleteByIds(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        //获取请求体数据
        BufferedReader reader = request.getReader();
        String s = reader.readLine();
        //将JSON字符串转为int[] 数组
        int[] ids = JSON.parseObject(s, int[].class);
        //2、调用service添加
        brandService.deleteByIds(ids);
        //3、响应成功标识  添加未完成则不会执行
        response.getWriter().write("success");
    }

4.4 前端实现

4.4.1获取id值

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

在批量删除标签中添加,@click点击事件,当点击批量删除时会触发,deleteByIds方法。this.multipleSelection是选中的所有的对象数组,从数组中获取id的值,将id的值添加到selectIds[ ]数组中。在 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;
}

4.4.2 发送异步请求

使用 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'
        });
    }
})

4.4.3 确定框

当一下删除许多元素时,可能会出现误删等情况,所以会出现一个判断的提示框,当点击确定时会进行删除,点击取消时会取消删除。

            /*根据id批量删除*/
            deleteByIds(){
                this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    //console.log(this.multipleSelection);
                    //创建id数组[1,2,3] 从this.multipleSelection获取即可
                    for (let i = 0; i < this.multipleSelection.length; i++) {
                        let selectionElement = this.multipleSelection[i];
                        this.selectIds[i] = selectionElement.id;
                    }
                    var _this = this;
                    //获取id数组后发送ajax请求
                    axios({
                        method: "post",
                        url: "http://localhost:8080/brand-case/brand/deleteByIds",
                        data:_this.selectIds
                    }).then(function (resp) {
                        if (resp.data=="success"){
                            _this.selectAll();
                            _this.$message({
                                message: '删除成功!',
                                type: 'success'
                            });
                        }
                    });
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });
            }

5 分页查询

5.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;

前后断数据交互
显示
前端向后端传递两个参数:

  1. 当前页码:currentPage
  2. 每页显示条数:pageSize

后端向前端传递参数:

  1. 当前页的数据:List
  2. 总共数据的条数:TotleCount

5.2 后端代码

显示

先创建一个PageBean类,来存储接受的每页的数据和总的页数。

  • 当前页需要展示的数据。我们在后端一般会存储到 List 集合中
  • 总共记录数。在上图页面中需要展示总的记录数,所以这部分数据也需要。

而这两部分需要封装到 PageBean 对象中,并将该对象转换为 json 格式的数据响应回给浏览器。
所以会在polo包下创建PageBean
代码如下:

package com.zpd.pojo;

import java.util.List;

/**
 * @Description
 * @Author zpd
 * @Date 2022/6/25
 */
public class PageBean<T> {
    //总数据条数
    private int TotalCount;
    //当前行数据
    private List<T> rows;

    public int getTotalCount() {
        return TotalCount;
    }

    public void setTotalCount(int totalCount) {
        TotalCount = totalCount;
    }

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

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

    @Override
    public String toString() {
        return "PageBean{" +
                "TotalCount=" + TotalCount +
                ", rows=" + rows +
                '}';
    }
}

5.2.1 Dao层

在BrabndMapper.java中写分页的方法和查询数据总条数的方法。

BrandMapper.java

    @Select("select * from tb_brand limit #{begin},#{size}")
    @ResultMap("brandResultMap")
    List<Brand> selectByPage(@Param("begin") int begin, @Param("size") int size);

    /**
     * 查询总条数
     * @return
     */
    int selectTotalCount();

BrandMapper.xml

    <select id="selectTotalCount" resultType="java.lang.Integer">
        select count(*) from tb_brand
    </select>

5.2.2 Service层

在BrandService接口中定义分页查询的方法。

PageBean<Brand> selectByPage(@Param("begin") int begin,@Param("size") int size);

在BrandServiceImpl中重写此方法,并进行业务逻辑实现

    @Override
    public PageBean<Brand> selectByPage(int begin, int size) {
        SqlSession sqlSession = factory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        //begin是开始页码索引
        begin = (begin-1) * size;
        //查询当前页的数据
        List<Brand> brands = mapper.selectByPage(begin, size);
        //查询总记录数量
        int totalCount = mapper.selectTotalCount();
        //将页面数据和总记录数量封装到PageBean中
        PageBean pageBean = new PageBean();
        pageBean.setRows(brands);
        pageBean.setTotalCount(totalCount);
        sqlSession.close();
        return pageBean;
    }

5.2.3 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 ServletException, IOException {
        
        String _begin = request.getParameter("begin");
        String _size = request.getParameter("size");
        //将接受到的String类型的数据转换为int类型
        int begin = Integer.parseInt(_begin);
        int size = Integer.parseInt(_size);
        //调用brandService查询
        PageBean<Brand> brandPageBean = brandService.selectByPage(begin, size);
        String jsonString = JSON.toJSONString(brandPageBean);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(jsonString);
    }

5.2.3 测试

在浏览器上输入网址,http://localhost:8080/brand-case/brand/selectByPage?begin=3&size=5,可以通过改变当前页码数begin和每页显示的条目数size来查看不同的页数数据。
最后得到的是每页的数据和全部的数据量
显示

5.3 前端实现

5.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:"post",
    url:"http://localhost:8080/brand-case/brand/selectByPage?currentPage=1&pageSize=5"
}).then(resp =>{
    //设置表格数据
    _this.tableData = resp.data.rows; // {rows:[],totalCount:100}
})

响应的数据中还有总记录数,要进行总记录数展示需要在页面绑定数据模型。在return中设置了totalCount的数据模,在then中传递了totalCount的数据。

 _this.totalCount = resp.data.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;
})

5.3.2 改变每页条目数

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

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

handleSizeChange(val) { //我们选择的是 ‘5条/页’ 此值就是 5.而我们选择了 `10条/页` 此值就是 10
    // 重新设置每页显示的条数
    this.pageSize  = val; 
    //调用 selectAll 函数重新分页查询数据
    this.selectAll();
}

5.3.3 改变当前页码

当我们改变页码时,需要重新发送异步请求。@current-change 就是页码发生变化时会触发的事件
而该事件绑定了一个 handleSizeChange 函数,整个逻辑如下:

handleCurrentChange(val) { //val 就是改变后的页码
    // 重新设置当前页码
    this.currentPage  = val;
    //调用 selectAll 函数重新分页查询数据
    this.selectAll();
}

最后重启服务器进行测试,点击不同的页码会进行换页,选择不同的每页的条数时也会跳转。

6 条件查询

需要满足一下三个条件:

  • 三个条件不需要全部填写
  • 条件之间的关系是and
  • 条件查询需要分页

6.1 后端实现

6.1.1 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);
  • begin 分页查询的起始
  • size 分页查询的每页条目数
  • brand 用来封装条件的对象

brand_name 字段和 company_name 字段需要进行模糊查询,所以需要使用 % 占位符。映射配置文件中 statement 书写如下:

<!--当brandName和companyName不为空或者不是空字符串时进行模糊查询-->
<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>

<!--与上面的条件一样,直接复制
但是这里的brand没有Param,所以不用brand调用-->
<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>

6.1.2 service实现

在BrandService接口中定义selectByPageAndCondition方法,代码如下:

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

在impl文件中重写此方法并进项业务处理。
将获取的Brand类中的brandName和compaName字符串两端加上%进行模糊查询。

    @Override
    public PageBean<Brand> selectByPageAndCondition(int begin, int size, Brand brand) {
        SqlSession sqlSession = factory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        //处理brand条件,模糊表达式
        String brandName = brand.getBrandName();
        if (brandName != null & brandName.length()>0){
            brandName = ("%"+brandName+"%");
            brand.setBrandName(brandName);
        }
        String companyName = brand.getCompanyName();
        if (companyName != null & companyName.length()>0){
            companyName = ("%"+companyName+"%");
            brand.setCompanyName(companyName);
        }
        //begin是开始页码索引
        begin = (begin-1) * size;
        //查询当前页的数据
        List<Brand> brands = mapper.selectByPageAndCondition(begin, size,brand);
        //查询总记录数量
        int totalCount = mapper.selectTotalCountByCondition(brand);
        //将页面数据和总记录数量封装到PageBean中
        PageBean pageBean = new PageBean();
        pageBean.setRows(brands);
        pageBean.setTotalCount(totalCount);

        sqlSession.close();
        return pageBean;
    }

6.1.3 servlet实现

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

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

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

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

  • 响应 json 数据

具体代码如下:

    /**
     * 分页条件查询
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void selectByPageAndCondition(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收当前页码  和  每页展示条数 url?begin=1&size=5
        request.setCharacterEncoding("utf-8");
        String _begin = request.getParameter("begin");
        String _size = request.getParameter("size");
        int begin = Integer.parseInt(_begin);
        int size = Integer.parseInt(_size);
        BufferedReader reader = request.getReader();
        String s = reader.readLine();
        //将JSON字符串转为Brand对象
        Brand brand = JSON.parseObject(s, Brand.class);
        PageBean<Brand> brandPageBean = brandService.selectByPageAndCondition(begin, size,brand);
        String jsonString = JSON.toJSONString(brandPageBean);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(jsonString);
    }

6.2 前端实现

  1. 查询表单绑定的查询对象模型
  2. 点击查询进行查询数据
            // 查询方法
            onSubmit() {
                //console.log(this.brand);
                this.selectAll();
            },
  1. 改进selecAll()
		selectAll(){
                    /*var _this = this;
                    axios({
                        method:"get",
                        url:"http://localhost:8080/brand-case/brand/selectAll"
                    }).then(function (resp) {
                        _this.tableData = resp.data;
                    })*/
                    /*查询分页的数据*/
                    var _this = this;
                    axios({
                        method:"post",
                        url:"http://localhost:8080/brand-case/brand/selectByPageAndCondition?begin="+_this.currentPage+"&size="+_this.pageSize,
                        data:this.brand
                    }).then(function (resp) {
                        //设置表格数据
                        _this.tableData = resp.data.rows; //{rows:[],totalCount}
                        //设置总记录数
                        _this.totalCount = resp.data.totalCount;
                    })
                },

7 代码优化

在前端的代码里,在axios中,每次都会用_this代替this。这样很麻烦,因为外面的this才是代表的Vue对象,而回调函数中的 this 表示的不是 vue 对象。这里我们可以使用 ECMAScript6 中的新语法(箭头函数)来简化这部分代码。
如下:

   			  axios({
                        method:"post",
                        url:"http://localhost:8080/brand-case/brand/selectByPageAndCondition?begin="+_this.currentPage+"&size="+_this.pageSize,
                        data:this.brand
                    }).then(resp =>{
                        this.tableData = resp.data.rows; //{rows:[],totalCount}
                        //设置总记录数
                        this.totalCount = resp.data.totalCount;
                    })
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值