JavaWeb实战:基础CRUD+批量删除+分页+条件


技术栈及相关参考资料:

  • Servlet基础
  • MVC模式和三层架构
  • Element

目录
  • 1、需求
  • 2、环境准备
    • 2.1 工程准备
    • 2.2 创建表
  • 3、查询所有功能
    • 3.1 后端实现
      • 3.1.1 Dao层方法实现
      • 3.1.2 Service层方法实现
      • 3.1.3 Servlet实现与Servlet优化
    • 3.2 前端实现
  • 4、添加功能
    • 4.1 后端实现
      • 4.1.1 Dao层方法实现
      • 4.1.2 Service层方法实现
      • 4.1.3 Serlvet实现,在 BrandServlet 中添加方法
    • 4.2 前端实现
  • 4、修改和删除功能
  • 5、批量删除功能
  • 6、分页功能
  • 7、条件查询

1、需求

  • 实现对品牌数据最基础的 CRUD 功能(文中仅演示了查询和添加)
  • 实现在所有数据页面对数据的批量删除功能
  • 实现分页展示功能
  • 实现条件查询功能

2、环境准备

2.1 工程准备

  • vue.js 和 element-ui 依赖

  • 引入 Axios.js 依赖

  • 包结构

  • Maven 依赖 pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    
      4.0.0
    
      priv.dandelion
      brand-case
      1.0-SNAPSHOT
      war
    
      
        8
        8
      
    
      
        
        
          junit
          junit
          4.11
          test
        
        
        
          org.mybatis
          mybatis
          3.5.5
        
        
        
          mysql
          mysql-connector-java
          5.1.46
        
        
        
          org.slf4j
          slf4j-api
          1.7.20
        
        
        
          ch.qos.logback
          logback-classic
          1.2.3
        
        
        
          ch.qos.logback
          logback-core
          1.2.3
        
        
        
          javax.servlet
          javax.servlet-api
          3.1.0
          provided
        
        
        
          javax.servlet.jsp
          jsp-api
          2.2
          provided
        
        
        
          jstl
          jstl
          1.2
        
        
        
          taglibs
          standard
          1.1.2
        
        
        
          com.alibaba
          fastjson
          1.2.62
        
      
    
      
        
          
            
            org.apache.tomcat.maven
            tomcat7-maven-plugin
            2.2
          
        
      
    
    
  • MyBatis 核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    
    
        
        
            
        
    
        
            
                
                
                
                
                    
                    
                    
                    
                    
                
            
        
        
        
            
        
    
    
  • 创建 SqlSession 工厂工具类

    package priv.dandelion.utils;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class SqlSessionFactoryUtils {
    
        // 提升作用域,用于再方法内进行返回
        private static SqlSessionFactory sqlSessionFactory;
    
        // 静态代码块会随着类的加载自动执行且只执行一次
        static {
            String resource = "mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }
    
        public static SqlSessionFactory getSqlSessionFactory() {
            return sqlSessionFactory;
        }
    }
    
  • 实体类 Brand.java

    package priv.dandelion.entity;
    
    public class Brand {
        // id 主键
        private Integer id;
        // 品牌名称
        private String brandName;
        // 企业名称
        private String companyName;
        // 排序字段
        private Integer ordered;
        // 描述信息
        private String description;
        // 状态:0:禁用  1:启用
        private Integer status;
    
        public Brand() {
        }
    
        public Brand(Integer id, String brandName, String companyName, Integer ordered, String description, Integer status) {
            this.id = id;
            this.brandName = brandName;
            this.companyName = companyName;
            this.ordered = ordered;
            this.description = description;
            this.status = 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 +
                    ", brand_name='" + brandName + '\'' +
                    ", company_name='" + companyName + '\'' +
                    ", ordered=" + ordered +
                    ", description='" + description + '\'' +
                    ", status=" + status +
                    '}';
        }
    }
    
  • Mapper 映射文件和 Mapper 接口

    <?xml version="1.0" encoding="UTF-8" ?>
    
    
    
        
        
            
            
        
    
    
    
  • 前端页面 brand.html

    
    
    
        
        Title
        
    
    
    
    查询 批量删除 新增 提交 取消

2.2 创建表

  • SQL脚本

    -- 删除tb_brand表
    drop table if exists tb_brand;
    -- 创建tb_brand表
    create table tb_brand
    (
        -- id 主键
        id           int primary key auto_increment,
        -- 品牌名称
        brand_name   varchar(20),
        -- 企业名称
        company_name varchar(20),
        -- 排序字段
        ordered      int,
        -- 描述信息
        description  varchar(100),
        -- 状态:0:禁用  1:启用
        status       int
    );
    -- 添加数据
    insert into tb_brand (brand_name, company_name, ordered, description, status)
    values 
           ('华为', '华为技术有限公司', 100, '万物互联', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
           ('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
           ('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
           ('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
           ('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
           ('华为', '华为技术有限公司', 100, '万物互联', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
           ('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
           ('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
           ('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
           ('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
           ('华为', '华为技术有限公司', 100, '万物互联', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
           ('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
           ('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
           ('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
           ('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
           ('华为', '华为技术有限公司', 100, '万物互联', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
           ('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
           ('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
           ('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
           ('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
           ('华为', '华为技术有限公司', 100, '万物互联', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
           ('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
           ('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
           ('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
           ('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
           ('华为', '华为技术有限公司', 100, '万物互联', 1),
           ('小米', '小米科技有限公司', 50, 'are you ok', 1),
           ('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
           ('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
           ('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
           ('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
           ('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1)
            ;
       
    
    SELECT * FROM tb_brand;
    

3、查询所有功能

3.1 后端实现

3.1.1 Dao层方法实现

package priv.dandelion.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Select;
import priv.dandelion.entity.Brand;

import java.util.List;


public interface BrandMapper {

    // 解决数据库与实体类明明不一致问题
    @ResultMap("brandResultMap")
    @Select("select * from tb_brand")
    List selectAll();

}

3.1.2 Service层方法实现

  • 接口

    package priv.dandelion.service;
    
    import priv.dandelion.entity.Brand;
    
    import java.util.List;
    
    public interface BrandService {
        /**
         * 查询所有
         * @return
         */
        List selectAll();
    }
    
  • 实现类

    package priv.dandelion.service.impl;
    
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import priv.dandelion.dao.BrandMapper;
    import priv.dandelion.entity.Brand;
    import priv.dandelion.service.BrandService;
    import priv.dandelion.utils.SqlSessionFactoryUtils;
    
    import java.util.List;
    
    public class BrandServiceImpl implements BrandService {
    
        SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
    
        @Override
        public List selectAll() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
            List brands = mapper.selectAll();
            sqlSession.close();
            return brands;
        }
    }
    

3.1.3 Servlet实现与Servlet优化

每个Servlet写一个类非常杂乱,不利于编写和管理

可以将其写到同一个类中,URL将更变为如http://localhost:8080/brand-case/brand/selectAll的形式

  1. 获取最后一段路径,即方法名称
  2. 通过反射获取方法对象,执行方法

注:

  • 不继承HttpServlet而进行自定义,并手动实现使用请求路径进行方法分发
  • 注意代码中this的使用方法(动态绑定
  • BaseServlet 负责替换 HttpServlet ,根据请求的最后一段路径进行方法分发,是 Servlet的父类

    package priv.dandelion.controller.servlet;
    
    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;
    
    /**
     * 替换HttpServlet
     *
     * 根据请求的最后一段路径进行方法分发
     */
    public class BaseServlet extends HttpServlet {
        // 重写service,根据请求路径进行方法分发
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 1. 获取请求路径
            String requestURI = req.getRequestURI();    // /brand-case/brand/selectAll
            // 获取请求路径的最后一段,即方法名
            int index = requestURI.lastIndexOf('/');
            // substring(index)截取字符串为左闭右开区间,不需要斜杠,index需要+1
            String methodName = requestURI.substring(index + 1);
    
            // 2. 获取BrandServlet的字节码对象,并获取方法的Method对象
            // 此处的this为动态绑定(Servlet入口在子类中,子类调用父类方法,程序执行到此处时,this代表调用父类方法的子类对象)
            Class<? extends BaseServlet> cls = this.getClass();
            // 获取方法的Method对象
            try {
                Method method = cls.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
    
                // 3. 执行方法
                method.invoke(this, req, resp);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
  • BrandServlet 关于 Brand 的 Servlet 类,将多个 Servlet 写在一个类中

    package priv.dandelion.controller.servlet;
    
    import com.alibaba.fastjson.JSON;
    import priv.dandelion.entity.Brand;
    import priv.dandelion.service.BrandService;
    import priv.dandelion.service.impl.BrandServiceImpl;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.util.List;
    
    @WebServlet("/brand/*")
    public class BrandServlet extends BaseServlet{
    
        private BrandService brandService = new BrandServiceImpl();
    
        public void selectAll(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            List brands = brandService.selectAll();
            // 将查询到的结果序列化为JSON
            String jsonString = JSON.toJSONString(brands);
            // 处理中文,使用utf-8
            resp.setContentType("text/json;charset=utf-8");
            // 写回数据
            resp.getWriter().write(jsonString);
        }
        
        // ...
        
    }
    

3.2 前端实现

  1. 页面加载完成后发送异步请求,获取列表数据(需要引入 Axios 的 JS 文件)
  2. 将数据设置到模型上
  • 代码

    
    
    
    
    
    
    

4、添加功能

4.1 后端实现

4.1.1 Dao层方法实现

4.1.2 Service层方法实现

  • 添加接口

    /**
     * 添加数据
     */
    boolean addBrand(Brand brand);
    
  • 实现接口

    @Override
    public boolean addBrand(Brand brand) {
        // 自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    
        // 判断用户名是否不存在
        final boolean flag = (null == mapper.selectByBrandName(brand.getBrandName()));
        if (flag) mapper.addBrand(brand);
    
        sqlSession.close();
        return flag;
    }
    

4.1.3 Serlvet实现,在 BrandServlet 中添加方法

public void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    // 接收前端提交的数据
    BufferedReader reader = req.getReader();
    String params = reader.readLine();
    // 反序列化为Brand对象
    Brand brand = JSON.parseObject(params, Brand.class);
    // 调用Service进行添加
    if (brandService.addBrand(brand)) resp.getWriter().write("success");
}

4.2 前端实现

  1. 点击提交按钮时,发送AJAX请求携带表单数据
  2. 获取数据,判断添加是否成功,关闭窗口,重新加载数据
  • 代码

        
    
    	
        
        
            批量删除
            新增
        
    
        
        
            
                
                    
                
    
                
                    
                
    
                
                    
                
    
                
                    
                
    
                
                    
                
                
                    提交
                    取消
                
            
        
    
        
    
    
    
    
    

4、修改和删除功能

  • Dao

    @Update("update tb_brand " +
            "set brand_name = #{brandName}," +
            "company_name = #{companyName}," +
            "ordered = #{ordered}," +
            "description = #{description}," +
            "status = #{status} " +
            "where id = #{id};")
    void updateBrand(Brand brand);
    
    @Delete("delete from tb_brand where id = #{id};")
    void deleteBrand(@Param("id") String id);
    
  • Service 新增接口方法并实现

    @Override
    public boolean updateBrand(Brand brand) {
        // 自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    
        // 判断用户名是否不存在
        boolean flag = (null == mapper.selectByBrandName(brand.getBrandName()));
        if (flag) mapper.updateBrand(brand);
    
        sqlSession.close();
        return flag;
    }
    
    @Override
    public void deleteBrand(String id) {
        // 自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    
        mapper.deleteBrand(id);
    
        sqlSession.close();
    }
    
  • Servlet

    public void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        // 接收前端提交的数据
        BufferedReader reader = req.getReader();
        String params = reader.readLine();
        // 反序列化为Brand对象
        Brand brand = JSON.parseObject(params, Brand.class);
        // 调用Service进行修改
        if (brandService.addBrand(brand)) resp.getWriter().write("success");
    }
    
    public void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        // 接收前端提交的数据
        String id = req.getParameter("id");
        // 调用Service进行添加
        brandService.deleteBrand(id);
    }
    
  • 前端页面

    
    

5、批量删除功能

  • Dao

    • Mapper接口

      void deleteByIds(@Param("ids") int[] ids);
      
    • Mapper映射文件

      
      
          
          delete from tb_brand where id in
          
              #{id}
          
      
      
  • Service

    @Override
    public void deleteByIds(int[] ids) {
        // 自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    
        mapper.deleteByIds(ids);
        sqlSession.close();
    }
    
  • Servlet

    public void deleteByIds(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        // 接收前端提交的数据
        BufferedReader reader = req.getReader();
        String params = reader.readLine();
        // 反序列化为Brand对象
        int[] ids = JSON.parseObject(params, int[].class);
        brandService.deleteByIds(ids);
        resp.getWriter().write("success");
    }
    
  • 前端页面

    
    
    
    批量删除 新增

6、分页功能

  • 用于分页查询的 JavaBean

    package priv.dandelion.entity;
    
    import java.util.List;
    
    /**
     * 分页查询的JavaBean
     */
    public class PageBean {
        // 总记录数
        private int totalCount;
        // 当前页数据
        private List rows;
    
        public int getTotalCount() {
            return totalCount;
        }
    
        public void setTotalCount(int totalCount) {
            this.totalCount = totalCount;
        }
    
        public List getRows() {
            return rows;
        }
    
        public void setRows(List rows) {
            this.rows = rows;
        }
    }
    
  • Dao

    // limit 开始索引, 查询的条目数
    // select * from tb limit 0, 5
    // 页面传递的参数为 起始条数和每一页显示的条数
    // 开始索引 = (当前页码 - 1) * 每一页显示的条数
    @Select("select * from tb_brand limit #{begin}, #{pageSize}")
    List selectByPage(@Param("begin") int begin, @Param("pageSize") int pageSize);
    
    @Select("select count(*) from tb_brand")
    int selectTotalCount();
    
  • Service

    @Override
    public PageBean selectByPage(int currentPage, int pageSize) {
    
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    
        // 计算开始索引
        int begin = (currentPage - 1) * pageSize;
    
        // 查询数据
        List rows = mapper.selectByPage(begin, pageSize);
        int totalCount = mapper.selectTotalCount();
    
        // 封装数据
        PageBean brandPageBean = new PageBean<>();
        brandPageBean.setRows(rows);
        brandPageBean.setTotalCount(totalCount);
    
        sqlSession.close();
        return brandPageBean;
    }
    
  • Servlet

    public void selectByPage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        // 接收当前页码和每页展示条数
        int currentPage = Integer.parseInt(req.getParameter("currentPage"));
        int pageSize = Integer.parseInt(req.getParameter("pageSize"));
        // 调用对应的Service进行查询
        PageBean brandPageBean = brandService.selectByPage(currentPage, pageSize);
        // 序列化为JSON
        String jsonString = JSON.toJSONString(brandPageBean);
        // 写回数据
        resp.setContentType("text/json;charset=utf-8");
        resp.getWriter().write(jsonString);
    }
    
  • 页面

    
    
        
        
        
    
    
    
    
    
    
    

7、条件查询

  • Dao

    • Mapper接口

      List selectByPageAndCondition(@Param("begin") int begin, @Param("pageSize") int pageSize, @Param("brand") Brand brand);
      int selectTotalCount();
      
    • Mapper映射文件

      
      
      
      
      
  • Service

    @Override
    public PageBean selectByPageAndCondition(int currentPage, int pageSize, Brand brand) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
    
        // 计算开始索引
        int begin = (currentPage - 1) * pageSize;
        // 模糊查询准备
        String brandName = brand.getBrandName();
        String companyName = brand.getCompanyName();
        if (brandName != null && brandName.length() > 0)
            brand.setBrandName("%"+ brandName +"%");
        if (companyName != null && companyName.length() > 0)
            brand.setCompanyName("%"+ companyName +"%");
    
        // 查询数据
        List rows = mapper.selectByPageAndCondition(begin, pageSize, brand);
        int totalCountByCondition = mapper.selectTotalCountByCondition(brand);
    
        // 封装数据
        PageBean brandPageBean = new PageBean<>();
        brandPageBean.setRows(rows);
        brandPageBean.setTotalCount(totalCountByCondition);
    
        sqlSession.close();
        return brandPageBean;
    }
    
  • Servlet

    public void selectByPageAndCondition(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        // 接收当前页码和每页展示条数
        int currentPage = Integer.parseInt(req.getParameter("currentPage"));
        int pageSize = Integer.parseInt(req.getParameter("pageSize"));
        // 获取查询条件对象
        BufferedReader reader = req.getReader();
        String params = reader.readLine();
        Brand brand = JSON.parseObject(params, Brand.class);
    
        // 调用对应的Service进行查询
        PageBean brandPageBean = brandService.selectByPageAndCondition(currentPage, pageSize, brand);
        // 序列化为JSON
        String jsonString = JSON.toJSONString(brandPageBean);
        // 写回数据
        resp.setContentType("text/json;charset=utf-8");
        resp.getWriter().write(jsonString);
    }
    
  • 页面

    
    
    查询