栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JavaWeb综合案例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaWeb综合案例

JavaWeb案例
  • 学习目标
  • 学习内容
  • 学习产出
  • 1.查询所有功能
    • 1.1 环境准备
    • 1.2 后端实现
    • 1.2 前端实现
  • 2 添加数据
    • 2.1 后端实现
    • 2.2 前端实现
  • 3 代码优化
  • 4 批量删除
    • 4.1 dao方法实现
    • 4.2 service方法实现
    • 4.3 servlet实现
    • 4.4 前端实现
      • 4.4.1获取id值
      • 4.4.2 发送异步请求
    • 4.4.3 确定框
  • 5 分页查询
    • 5.1 分页查询sql
    • 5.2 后端代码
      • 5.2.1 Dao层
      • 5.2.2 Service层
      • 5.2.3 servlet实现
      • 5.2.3 测试
    • 5.3 前端实现
      • 5.3.1 selectAll 代码改进
      • 5.3.2 改变每页条目数
      • 5.3.3 改变当前页码
  • 6 条件查询
    • 6.1 后端实现
      • 6.1.1 Dao层
      • 6.1.2 service实现
      • 6.1.3 servlet实现
    • 6.2 前端实现
  • 7 代码优化

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

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

学习产出

综合案例资料

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

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

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

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

  • 请求需要携带参数吗?

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

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

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

1.2 后端实现

BranadMapper

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

@Select("select * from tb_brand")
List selectAll();

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





    
        
        
    

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

@Select("select * from tb_brand")
@ResultMap("brandResultMap")
List selectAll();

BrandService

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

public interface BrandService {

    
    List selectAll();
}

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

import java.util.List;


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

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

Servlet
该 servlet 逻辑如下:

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

在web下创建SelectAllServlet
代码如下

package com.zpd.web; 

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 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代码

    
    @Insert("insert into tb_brand values(null,#{brandName},#{companyName},#{ordered},#{desription},#{status})")
    void add(Brand brand);

BrandMapper代码

    
    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 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
        
        //2.2、获取方法  Method对象
        Class 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 对象,所以指定方法的参数为 HttpServletRequest 和 HttpServletResponse,那么 BrandServlet 代码就可以改进为:

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

BrandMapper.xml

    
       delete from tb_brand where id in
        
            #{id}
        
    
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 字符串表示成功
    
    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 确定框

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

            
            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;


public class PageBean {
    //总数据条数
    private int TotalCount;
    //当前行数据
    private List rows;

    public int getTotalCount() {
        return TotalCount;
    }

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

    public List getRows() {
        return rows;
    }

    public void setRows(List 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 selectByPage(@Param("begin") int begin, @Param("size") int size);

    
    int selectTotalCount();

BrandMapper.xml

    
    select *
    from tb_brand
    
        
            and  brand_name like #{brand.brandName}
        

        
            and  company_name like #{brand.companyName}
        

        
            and  status = #{brand.status}
        
    
    limit #{begin} , #{size}




6.1.2 service实现

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

  
    PageBean selectByPageAndCondition(@Param("begin") int begin,@Param("size") int size,@Param("brand")Brand brand);

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

    @Override
    public PageBean 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 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 数据

具体代码如下:

    
    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 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:"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;
                    })
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1000130.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号