暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JavaWeb阶段之MVC+Web版的CRUD

java学途 2021-06-21
402


不点蓝字,我们哪来故事?




回顾:

浏览器发出请求——>Servlet处理请求——>DAO访问数据库——>数据库中的表

以查询学生信息为例:

浏览器发出请求:

    请求什么资源?

        静态资源:HTML,图片等

        动态资源:Servlet,JSP

JSP:jsp主要负责数据的显示,显示的位置在哪?

Servlet:

  • 获取请求数据

  • 调用业务方法处理请求,并共享数据

  • 控制页面跳转

在该请求中,应该准备一个Servlet。将数据准备好;再跳转到jsp中,在JSp中使用EL表达式和JSTL标准标签库中的数据呈现给用户。


使用什么作用域来共享数据?

需要在Servlet和JSP之间共享查询到的数据信息,哪一个作用域对象合适再进行选择。根据数据数据共享来选择作用域对象。

以后开发中,还可以根据跳转的资源所在位置来决定页面跳转的方式:

  • 访问WEB-INF中的资源:请求转发;

  • 访问WEB-INF外的资源:重定向;


在JSP中如何获得共享数据?EL+JSTL

    使用EL表达式从Servlet中获得共享数据;

    在使用JSTL来操作(遍历)获取到的信息。


MVC设计思想

模式1(Model 1):

  以JSP为中心.

  JSP + JavaBean.


JSP的职责:

  1.  界面输出.

  2. 接受请求参数

  3. 调用业务方法,处理请求

  4. 控制界面跳转.

优势: 适用于简单的,快速开发.

劣势: 没有体现出责任分离原则.

模式2(Model2):解决了Model1存在的责任不分离的问题.

   以Servlet为中心(所有请求都发送给Servlet):

   JSP + Servlet + JavaBean .


servlet/JSP/JavaBean的职责:

JSP:    界面输出.

Servlet:

  1. 接受请求参数

  2. 调用业务方法,处理请求

  3. 控制界面跳转.

JavaBean:

     封装业务操作,可重复使用.



MVC模式:面试题

 目的:责任分离,把业务代码从视图中剥离出来。

 早期运用于CS领域(桌面程序).

M:Model:模型对象(封装业务操作,算法,可重复使用,JavaBean);

V:View:视图(界面,JSP,HTML);

C:Controler:控制器(控制界面跳转,Servlet.)

三层架构

Web开发中的最佳实践:分层开发模式(技术层面的"分而治之").

三层架构(3-tier architecture) :

通常意义上的三层架构就是将整个业务应用划分为:表现层、业务逻辑层、数据访问层。区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。

  • 表现层(Predentation Layer):MVC,负责处理与界面交互的相关操作(Struts2/Spring MVC) 直接与用户交互的;

  • 业务层(Business Layer):service,负责复杂的业务逻辑计算和判断(Spring)

  • 持久层(Persistent Layer) :ORM,负责将业务逻辑数据进行持久化存储(Hibernate/MyBatis)

开发顺序:持久层——>业务层——>表现层


后台CRUD开发

操作数据库表

项目后台结构:

代码实现:

测试先行.(测试代码没有收集)

    针对dao和service分别取测试.完成实现类,实现一个,测试一个.

IProductDAO.java

    public interface IProductDAO {
    // 添加
    void insert(Product p);
    // 删除
    void delete(Long id);
    // 修改
    void update(Product p);
    // 查询
    Product selectOne(Long id);
    // 查询所有
    List<Product> selectAll();
    }

    ProductImpl.java

      public class ProductImpl implements IProductDAO {


      @Override
      public void insert(Product pro) {
      // 获得会话对象,添加
      SqlSession session = MyBatisUtil.getSqlSession();
      session.insert("cn.wolfcode.pmis.maaper.ProductMapper.insert", pro);
      session.commit();
      session.close();
      }


      @Override
      public void delete(Long id) {
      // 获得会话对象,添加
      SqlSession session = MyBatisUtil.getSqlSession();
      session.delete("cn.wolfcode.pmis.maaper.ProductMapper.delete", id);
      session.commit();
      session.close();


      }


      @Override
      public void update(Product pro) {
      // 获得会话对象,添加
      SqlSession session = MyBatisUtil.getSqlSession();
      session.update("cn.wolfcode.pmis.maaper.ProductMapper.update", pro);
      session.commit();
      session.close();


      }


      @Override
      public Product selectOne(Long id) {
      // 获得会话对象,添加
      SqlSession session = MyBatisUtil.getSqlSession();
      Product one = session.selectOne("cn.wolfcode.pmis.maaper.ProductMapper.selectOne", id);
      //查询不需要提交事务
      //session.commit();
      //一定要释放资源,否则连接池会被使用完
      session.close();
      return one;
      }


      @Override
      public List<Product> selectAll() {
      // 获得会话对象,添加
      SqlSession session = MyBatisUtil.getSqlSession();
      List<Product> list = session.selectList("cn.wolfcode.pmis.maaper.ProductMapper.selectAll", null);
      //查询不需要提交事务
      //session.commit();
      session.close();
      return list;
      }


      }

      Product.java

        @AllArgsConstructor
        @NoArgsConstructor
        @Setter
        @Getter
        @ToString
        public class Product {
        //字段名与数据库中的表列名相同
        private Long id;
        private String productName;
        private Long dir_id;
        private Double salePrice;
        private String supplier;
        private String brand;
        private Double cutoff;
        private Double costPrice;

        }

        MyBatisUtil.java

          public class MyBatisUtil {
          //私有化构造器
          private MyBatisUtil() {}
          static SqlSessionFactory factory = null;
          static {
          //获得sql会话工厂对象
          try {
          factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
          } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          }
          //获得SQL会话对象的方法
          public static SqlSession getSqlSession() {
          //返回会话连接对象
          return factory.openSession();
          }
          }

          ProductMapper.xml

            <?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">

            <!-- namespace:区分不同Mapper中的项目id的SQL -->
            <mapper namespace="cn.wolfcode.pmis.maaper.ProductMapper">
            <insert id="insert" useGeneratedKeys="true" keyProperty="id">
            insert into product(productName,dir_id,salePrice,supplier,brand,cutoff,costPrice)
            values(#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice})
            </insert>
            <delete id="delete">
            delete from product where id = #{id}
            </delete>
            <update id="update">
            update product set productName=#{productName},
            dir_id=#{dir_id},
            salePrice=#{salePrice},
            supplier=#{supplier},
            brand=#{brand},
            cutoff=#{cutoff},
            costPrice=#{costPrice}
            where id = #{id}
            </update>
            <select id="selectOne" resultType="Product">
            select * from product where id = #{id}
            </select>
            <select id="selectAll" resultType="Product">
            select * from product
            </select>

            </mapper>

            mybatis-config.xml

              <?xml version="1.0" encoding="UTF-8"?>
              <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
              <configuration>
              <properties resource="pd.properties"></properties>
              <typeAliases>
              <package name="cn.wolfcode"/>
              </typeAliases>
              <environments default="crud">
              <environment id="crud">
              <transactionManager type="JDBC"></transactionManager>
              <dataSource type="POOLED">
              <property name="driver" value="${driverClassName}" >
              <property name="url" value="${url}" >
              <property name="username" value="${username}" >
              <property name="password" value="${password}" >
              </dataSource>
              </environment>
              </environments>
              <mappers>
              <mapper resource="cn\wolfcode\pmis\maaper\ProductMapper.xml"/>
              </mappers>
              </configuration>

              pd.properties

                driverClassName=com.mysql.jdbc.Driver
                url=jdbc:mysql:///java_web
                username=root
                password=root



                前台CRUD开发

                分析web的crud到底有哪些操作/请求?

                1.查询商品列表

                    一定要使用一个servlet去查询所有的商品.

                    ProductListServlet

                2.删除指定的商品

                    一定要使用servlet去删除数据库中的数据.

                    ProductDeleteServlet

                3.点击编辑和添加都是进入到可编辑的界面

                    一定要使用servlet去跳转到编辑界面.

                    ProductInputServlet

                4.在编辑界面,点击保存.

                    一定要使用一个servlet去处理保存或者更新操作.

                    ProductSaveOrUpdateServlet


                上面的设计的问题:

                如果我们操作非常多,可能会使用非常多的servlet去处理请求.

                导致类的个数爆炸式增长.不好维护.

                在实际开发中,我们通常是在一个Servlet中完成对一个实体(表)的增删改查的处理


                解决方案:(请求分发

                使用一个类来处理一张表的操作.

                在service方法中,想要去区分开到底是哪个请求,可以增加一个参数来区分.

                cmd:使用该参数来区分是何种请求,不同的值,就表示不同的请求.

                • cmd=delete:删除操作

                • cmd=input:点击添加或者编辑进入到相同的输入界面

                • cmd=saveOrUpdate:在输入界面点击提交按钮的操作

                • 默认就是列表界面

                1. 需要知道用户到底是发的什么请求

                  1. localhost/product?cmd=input  跳转到录入页面

                  2. localhost/product?cmd=save  保存商品信息

                  3. localhost/product?cmd=update  修改商品信息,   通常是和save操作合并在一起来处理

                  4. localhost/product?cmd=delete&a=1&b=2  删除指定的商品

                  5. localhost/product?cmd=list  查询所有商品信息


                  在Servlet中获取到用户请求中传递的cmd,根据cmd的值来调用响应的方法来处理请求


                查询流程:



                删除流程:



                点击添加按钮进入编辑界面:


                添加功能的流程(易错点)



                在这个过程中,需要单独执行两个请求来完成

                1. 用户在点击添加按钮(超链接)时,是想到一个录入页面中去填写自己的数据,所以我们需要在这个时候去跳转到录入页面

                  /product?cmd=input-----> input.jsp

                2. 用户在录入完数据之后,再提交表单将数据保存到数据库中

                  /product?cmd=saveOrUpdate

                  1. 接收用户表单中填写的数据,将数据封装到Product对象

                  2. 调用业务方法处理保存操作:service.save(Product对象)

                  3. 再重新查询一次,将最新数据显示到页面上


                商品信息编辑的流程


                该编辑操作需要两个请求来完成

                1. 点击编辑按钮,跳转到录入页面, 这里我们需要将编辑之前的数据填充到表单中

                  1. 如果不填充原始数据,那么用户需要填写所有信息(包括他不修改的数据)

                  2. 给表单元素设置数据使用他的value属性来实现

                2. 用户根据自己的需要修改表单中的数据,然后提交表单

                  1. 此时需要把修改之后的数据给更新到数据库中

                  2. 更新之后再将最新的数据显示到页面中

                在saveOrUpdate方法中同时完成保存和更新功能,此时存在的问题是:如何区分用户保存还是更新?

                    保存时,用户不用传递id, 更新时,用户必须要传递id

                所以在这里我们就可以根据id是否有值来判断用户是保存还是更新

                  1. 在表单中添加一个隐藏域来保存id的值
                  2. 在servlet中接收id,判断是否有值
                  1. 有:更新,记得将id封装到商品对象中
                  2. 没有保存


                  点击提交按钮的流程:



                  错误定位:

                  找问题的原则:

                  对于web应用,出现了数据错误问题,一定要第一时间定位到错误的位置.第一时间确定问题是出在前台界面,或者是后台数据库.

                  1. 在浏览器中打开F12,查看数据是否传递给了服务器,如果在请求中已经传递了数据,那么就可以不用看前台界面了.问题是后台的代码.

                  2. 在servlet的service方法中,调用业务方法的地方打断点,确定到底是参数封装问题,还是调用业务的问题.这样可以将问题定位到是否是数据库问题.

                  注:

                  web应用是交给tomcat管理的,所以直接打断点,只要有请求过来,service就可以被断掉.


                  项目前台结构:


                  IProductService.java

                    public interface IProductService {
                    // 添加
                    void save(Product pro);
                    // 删除
                    void delete(Long id);
                    // 修改
                    void update(Product pro);
                    // 查询
                    Product get(Long id);
                    // 查询所有
                    List<Product> listAll();
                    }

                    ProductServiceImpl.java

                      public class ProductServiceImpl implements IProductService {
                      //获得DAO对象
                      private IProductDAO dao = new ProductImpl();


                      @Override
                      public void save(Product pro) {
                      dao.insert(pro);
                      }


                      @Override
                      public void delete(Long id) {
                      dao.delete(id);
                      }


                      @Override
                      public void update(Product pro) {
                      dao.update(pro);
                      }


                      @Override
                      public Product get(Long id) {
                      return dao.selectOne(id);
                      }


                      @Override
                      public List<Product> listAll() {
                      return dao.selectAll();
                      }


                      }

                      ProductServlet.java

                        //在这里完成对表的CRUD
                        @WebServlet("/product")
                        public class ProductServlet extends HttpServlet {
                        private static final long serialVersionUID = 1L;


                        // 获得业务对象
                        private IProductService service = new ProductServiceImpl();


                        @Override
                        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        // 设置编码方式
                        req.setCharacterEncoding("UTF-8");


                        // 获取用户请求中的cmd中的值
                        String cmd = req.getParameter("cmd");


                        // 请求分发:判断请求cmd的值,做不同的处理
                        if ("delete".equals(cmd)) {
                        delete(req, resp);
                        } else if ("input".equals(cmd)) {
                        input(req, resp);
                        } else if ("saveOrUpdate".equals(cmd)) {
                        saveOrUpdate(req, resp);
                        } else {// 默认执行全部查询
                        list(req, resp);
                        }
                        }


                        // 处理删除的请求
                        protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        /*
                        * 删除操作流程: 1.调用删除方法 2.删除后要显示删除之后所有商品的信息(可以在调listAll())
                        * 我们直接跳转到ProductServlet的product页面, product资源不在WEB-INF下,只能使用重定向的方式才能访问到
                        */
                        // 接收用户传过来的id
                        String id = req.getParameter("id");
                        // 传参要将String的id转化成Long
                        service.delete(Long.valueOf(id));
                        // 跳转
                        // list(req, resp);
                        // 删除成功以后,访问的资源不再WEB—INF下,用重定向
                        resp.sendRedirect("/product");
                        }


                        // 处理页面跳转:在添加和修改的时候需要跳转到用户录入信息的界面
                        protected void input(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        // 需要编辑的商品信息,根据用户传递的id来获取到对应的商品
                        String id = req.getParameter("id");
                        // 将获得到的id共享给input.jsp
                        if (hasLength(id)) {
                        // 获得该id对象
                        Product p = service.get(Long.valueOf(id));
                        // 分享该对象
                        req.setAttribute("p", p);
                        }


                        // 跳转到input.jsp
                        // input.jsp在WEB-INF下,所以用请求转发
                        req.getRequestDispatcher("/WEB-INF/views/product/input.jsp").forward(req, resp);


                        }


                        // 查询所有商品
                        protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        // 查询所有,没有参数,不用接收
                        // 调用业务方法
                        List<Product> products = service.listAll();
                        // 将查询的数据共享给JSp,因为数据只在当前请求中,所以用Request
                        req.setAttribute("products", products);
                        // 数据显示————>页面跳转,作用域是Request,所以用请求转发
                        req.getRequestDispatcher("/WEB-INF/views/product/list.jsp").forward(req, resp);
                        }


                        // 保存或修改:处理保存或更新的请求
                        protected void saveOrUpdate(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                        /*
                        * 保存操作的流程:(有两个请求) 1.点击添加按钮跳转 2.跳转到录入界面填写数据 3.填写完成后提交表单将数据保存到数据库中
                        */
                        // 保存:拿到用户输入商品中的值,封装到Product对象中
                        String productName = req.getParameter("productName");
                        String salePrice = req.getParameter("salePrice");
                        String supplier = req.getParameter("supplier");
                        String brand = req.getParameter("brand");
                        String cutoff = req.getParameter("cutoff");
                        String costPrice = req.getParameter("costPrice");
                        String dir_id = req.getParameter("dir_id");
                        // 封装数据到对象
                        Product p = new Product();//创建空对象
                        // 封装数据非String的参数要做类型转换,所以需要先判断字符串是否为空(空字符串转化时出现数据转化异常)
                        p.setProductName(productName);
                        if (hasLength(salePrice)) {
                        p.setSalePrice(Double.valueOf(salePrice));
                        }
                        p.setSupplier(supplier);
                        p.setBrand(brand);
                        if (hasLength(cutoff)) {
                        p.setCutoff(Double.valueOf(cutoff));
                        }
                        if (hasLength(costPrice)) {
                        p.setCostPrice(Double.valueOf(costPrice));
                        }
                        if (hasLength(dir_id)) {
                        p.setDir_id(Long.valueOf(dir_id));
                        }
                        //共享这个对象到list.jsp中,最终呈现出来
                        req.setAttribute("p", "p");


                        //此处注意:修改和保存操作区分是看id是否有值来决定调用哪个业务方法的
                        // 获取id
                        String id = req.getParameter("id");
                        if (hasLength(id)) {
                        //编辑操作:
                        // 更新之后要将id封装到对象中,才能保存到数据库
                        p.setId(Long.valueOf(id));
                        // 调用更新操作
                        service.update(p);
                        } else {//如果id为空,就执行保存业务
                        // 保存操作
                        // 调用业务保存方法
                        service.save(p);
                        }


                        // 跳转到ProductServlet中的list,显示最后结果
                        resp.sendRedirect("/product");
                        }


                        // 判断字符串是否为空
                        private boolean hasLength(String str) {
                        return str != null && str.trim().length() > 0;
                        }


                        }

                        list.jsp

                          <%@ page language="java" contentType="text/html; charset=UTF-8"
                          pageEncoding="UTF-8"%>
                          <!-- 导包JSTL -->
                          <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
                          <!DOCTYPE html>
                          <html>
                          <head>
                          <meta charset="UTF-8">
                          <title>商品信息列表</title>
                          </head>
                          <body>
                          <h2>商品信息列表</h2>
                          <!-- 添加:由servlet的input方法指定跳转到input.jsp页面 -->
                          <a href="/product?cmd=input">添加</a>
                          <table width="80%" border="1" cellpadding="0" cellspacing="0">
                          <tr>
                          <th>序号</th>
                          <th>编号</th>
                          <th>商品名称</th>
                          <th>商品类别</th>
                          <th>售价</th>
                          <th>生产商</th>
                          <th>品牌</th>
                          <th>折扣</th>
                          <th>成本价</th>
                          <th>操作</th>
                          </tr>


                          <!-- 遍历分享数据 products-->
                          <c:forEach items="${products}" var="p" varStatus="vs">
                          <!-- 将商品信息放到tr中对应的td中 -->
                          <tr align="center">
                          <td>${vs.count}</td>
                          <td>${p.id}</td>
                          <td>${p.productName}</td>
                          <td>${p.dir_id}</td>
                          <td>${p.salePrice}</td>
                          <td>${p.supplier}</td>
                          <td>${p.brand}</td>
                          <td>${p.cutoff}</td>
                          <td>${p.costPrice}</td>
                          <td>
                          <!-- 编辑:由servlet的input方法指定跳转到input.jsp页面 -->
                          <a href="/product?cmd=input&id=${p.id}">编辑</a> |
                          <!-- 删除操作:跳转回servlet类中执行删除的方法,删除方法需要接收id -->
                          <a href="/product?cmd=delete&id=${p.id}">删除</a></td>
                          </tr>
                          </c:forEach>


                          </table>
                          </body>
                          </html>


                          input.jsp

                            <%@ page language="java" contentType="text/html; charset=UTF-8"
                            pageEncoding="UTF-8"%>
                            <!DOCTYPE html>
                            <html>
                            <head>
                            <meta charset="UTF-8">
                            <title>商品信息录入页面</title>
                            </head>
                            <body>
                            <h2>商品信息录入页面</h2>
                            <!-- 表单提交之后:要将数据保存到数据库 -->
                            <form action="/product?cmd=saveOrUpdate" method="post">
                            <!-- 用户不关心id值,可没可以设置为只读不许修改,或者直接隐藏域让用户眼不见心不烦 -->
                            <input name="id" value="${p.id}" type="hidden"/>

                            <!-- 一定要设置name,为了Servlet里取值封装到对象中 -->
                            <!-- value是在编辑时在Servlet中get()的商品对象共享的,
                            在此处获得对象的信息在对应的输入框中,让用户选择性修改
                            -->
                            名 称:<input name="productName" value="${p.productName}"/><br/>
                            售 价:<input name="salePrice" value="${p.salePrice}"/><br/>
                            生产商:<input name="supplier" value="${p.supplier}"/><br/>
                            品 牌:<input name="brand" value="${p.brand}"/><br/>
                            折 扣:<input name="cutoff" value="${p.cutoff}"/><br/>
                            成 本:<input name="costPrice" value="${p.costPrice}"/><br/>
                            分 类:
                            <!-- select元素:提交到服务端的值是什么?
                            如果option元素没有使用value来指定值,那么值就是option之间的文本
                            如果option元素有使用value来指定值,那么值就是option的value属性的值

                            根据商品的dir_id来判断:哪个option元素上应该添加selected属性
                            -->
                            <select name="dir_id">
                            <option value="1" ${p.dir_id == 1 ? "selected" : ""}>鼠标</option>
                            <option value="2" ${p.dir_id == 2 ? "selected" : ""}>有线鼠标</option>
                            <option value="3" ${p.dir_id == 3 ? "selected" : ""}>无线鼠标</option>
                            <option value="4" ${p.dir_id == 4 ? "selected" : ""}>其他</option>
                            </select>
                            <br/>
                            <!-- 提交按钮 -->
                            <input type="submit" value="提交保存"/>
                            </form>
                            </body>
                            </html>


                            ————  e n d ————


                            觉得文章不错,欢迎点在看转发,长按识别二维码关注公众号 java学途,了解更多精彩。




                            点在看,让更多看见。




                            文章转载自java学途,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                            评论