基于JavaWeb实现的书城项目:阶段四

MVC概念

MVC 全称:Model 模型、 View 视图、 Controller 控制器。

MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。

View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作—— JSP/HTML。

Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。 转到某个页面。或者是重定向到某个页面。

Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码—— JavaBean/domain/entity/pojo。

MVC 是一种思想 MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnvsZsdl-1644134830862)(C:/Users/12709/AppData/Roaming/Typora/typora-user-images/image-20211119144353903.png)]

MVC 的作用还是为了降低耦合。让代码合理分层。方便后期升级和维护。

第四阶段 图书模块

1. 编写图书模块的数据库表

从准备的页面分析,可以观察到图书管理页面中,一本图书有图书名称、作者名称、价格、库存、销量这些属性:
在这里插入图片描述

有个需要注意的地方,图书在购物页面展示的时候,是具有封面的,也就是说,在编写数据库表时,需要一个字段来存放图书封面的路径!

-- 创建图书模块的数据库表
CREATE TABLE `t_book`(
`id` INT AUTO_INCREMENT,
`name` VARCHAR(100),
`price`	DECIMAL(11,2),
`author` VARCHAR(100),
`sales`	INT,
`stock` INT,
`img_path` VARCHAR(200),
PRIMARY KEY `id`(`id`)
)
-- 插入初始化测试数据
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'java 从入门到放弃' , '邓国盛' , 80 , 9999 , 9 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '木虚肉盖饭' , '小胖' , 16, 1000 , 50 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '追求众多女孩却从未成功' , '鱿鱼丝' , 9.9, 12 , 53 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '如何优雅地凹蛇' , '锐宝' , 66.5, 125 , 535 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'Java 编程思想' , '阳哥' , 99.5 , 47 , 36 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'JavaScript 从入门到精通' , '王婷' , 9.9 , 85 , 95 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'cocos2d-x 游戏编程入门' , '邓国盛' , 49, 52 , 62 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'C 语言程序设计' , '谭浩强' , 28 , 52 , 74 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'Lua 语言程序设计' , '雷丰阳' , 51.5 , 48 , 82 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '西游记' , '罗贯中' , 12, 19 , 9999 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '水浒传' , '施耐庵' , 33.05 , 22 , 88 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '操作系统原理' , '刘优' , 133.05 , 122 , 188 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '数据结构 java 版' , '封大神' , 173.15 , 21 , 81 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'UNIX 高级环境编程' , '乐天' , 99.15 , 210 , 810 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'javaScript 高级编程' , '邓国盛' , 69.15 , 210 , 810 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '23种设计模式' , '邓国盛' , 89.15 , 20 , 10 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '论看本子的自我修养' , 'Ayin' , 88.15 , 20 , 80 , 'static/img/default.jpg');

2. 编写图书模块的JavaBean

public class Book {
    private Integer id;
    private String name;
    private BigDecimal price;
    private String author;
    private Integer salves;
    private Integer shock;
    private String imgPath = "static/img/default.jpg";

    public Book() {
    }

    public Book(Integer id, String name, BigDecimal price, String author, Integer salves, Integer shock, String imgPath) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.author = author;
        this.salves = salves;
        this.shock = shock;
        if (imgPath != null && "".equals(imgPath)) {
            this.imgPath = imgPath;
        }
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Integer getSalves() {
        return salves;
    }

    public void setSalves(Integer salves) {
        this.salves = salves;
    }

    public Integer getShock() {
        return shock;
    }

    public void setShock(Integer shock) {
        this.shock = shock;
    }

    public String getImgPath() {
        return imgPath;
    }

    public void setImgPath(String imgPath) {
        if (imgPath != null && "".equals(imgPath)) {
            this.imgPath = imgPath;
        }
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", author='" + author + '\'' +
                ", salves=" + salves +
                ", shock=" + shock +
                ", imgPath='" + imgPath + '\'' +
                '}';
    }
}

图书模块的JavaBean与用户模块有所区别,因为封面要求不能为空,于是在封面路径的变量中需要有一个默认值,也就是默认的封面,在设置封面路径的时候,也要防止传入一个空串或者null!

3. 编写图书模块的Dao层和Dao测试

BookDao接口:

public interface BookDao {
     /**
      * 添加图书
      * @param book
      * @return 如果返回-1,则表示添加失败
      */
     int addBook(Book book);

     /**
      * 更改图书信息
      * @param book
      * @return 返回数据库表受影响的行,如果返回-1,则表示更改失败
      */
     int updateBook(Book book);

     /**
      * 删除图书
      * @param id 图书Book的id
      * @return 返回数据库表受影响的行,如果返回-1,则表示删除失败
      */
     int deleteBookById(Integer id);

     /**
      * 按照图书ID查找指定图书
      * @param id
      * @return 返回指定图书
      */
     Book queryBookById(Integer id);

     /**
      * 查找全部图书
      * @return 返回图书List<book>集合
      */
     List<Book> queryBooks();
}

BookDaoImpl实现类:

public class BookDaoImpl extends BaseDao implements BookDao {
    @Override
    public int addBook(Book book) {
        String sql = "INSERT INTO t_book(`name`, `price`, `author`, `sales`, `stock`, `img_path`) values(?, ?, ?, ?, ?, ?)";
        return update(sql, book.getName(), book.getPrice(), book.getAuthor(), book.getSales(), book.getStock(), book.getImgPath());
    }

    @Override
    public int updateBook(Book book) {
        String sql = "UPDATE `t_book` SET `name`=?, `price`=?, `author`=?, `sales`=?,`stock`=?,`img_path`=? WHERE id = ?";
        return update(sql, book.getName(), book.getPrice(), book.getAuthor(), book.getSales(), book.getStock(), book.getImgPath(), book.getId());
    }

    @Override
    public int deleteBookById(Integer id) {
        String sql = "DELETE FROM `t_book` WHERE `id` = ?";
        return update(sql, id);
    }

    @Override
    public Book queryBookById(Integer id) {
        String sql = "SELECT `id`,`name`,`price`,`author`,`sales`,`stock`,`img_path` imgPath FROM `t_book` WHERE `id` = ?";
        return queryForOne(Book.class, sql, id);
    }

    @Override
    public List<Book> queryBooks() {
        String sql = "SELECT `id`,`name`,`price`,`author`,`sales`,`stock`,`img_path` imgPath FROM `t_book`";
        return queryForList(Book.class, sql);
    }
}

需要注意的是,在数据表中,对图书封面路径的字段命名为img_path,而图书类中,对图书封面路径的变量命名为imgPath,为了保证命名统一,防止返回null,于是在进行查找操作时,需要对img_path字段起一个别名imgPath!

4. 编写图书模块的Service层和业务测试

编写BookService接口:

public interface BookService {
    /**
     * 添加图书
     * @param book
     * @return 如果返回-1,则表示添加失败
     */
    int addBook(Book book);

    /**
     * 更改图书信息
     * @param book
     * @return 返回数据库表受影响的行,如果返回-1,则表示更改失败
     */
    int updateBook(Book book);

    /**
     * 删除图书
     * @param id 图书Book的id
     * @return 返回数据库表受影响的行,如果返回-1,则表示删除失败
     */
    int deleteBookById(Integer id);

    /**
     * 按照图书ID查找指定图书
     * @param id
     * @return 返回指定图书
     */
    Book queryBookById(Integer id);

    /**
     * 查找全部图书
     * @return 返回图书List<book>集合
     */
    List<Book> queryBooks();
}

编写BookServiceImpl实现类:

public class BookServiceImpl implements BookService {
    BookDao bookDao = new BookDaoImpl();
    @Override
    public int addBook(Book book) {
        return bookDao.addBook(book);
    }

    @Override
    public int updateBook(Book book) {
        return bookDao.updateBook(book);
    }

    @Override
    public int deleteBookById(Integer id) {
        return bookDao.deleteBookById(id);
    }

    @Override
    public Book queryBookById(Integer id) {
        return bookDao.queryBookById(id);
    }

    @Override
    public List<Book> queryBooks() {
        return bookDao.queryBooks();
    }
}

从以上代码实例可以发现,Service层的编写和Dao层的编写大致相同,但是在概念上有所区别:

Service层与Web层进行交互,而Dao层与数据库进行交互,Service层可以和Dao层进行交互,但是Service层并不可以直接操作数据库,因为JavaEE的三层架构之间只能相邻架构进行交互,而不能跨层进行交互!

简单来说,JavaEE三层架构是一种项目的设计理念,虽然可以进行跨层操作,但是日后的维护和更新会变得非常困难,很有可能变成动一处地方而牵扯全局的场面!

5. 编写图书模块的Web层和页面联调测试

图书列表的实现

在图书管理页面中,需要将数据库中存放的图书数据展现出来,按照具体实现步骤,应该先将图书数据取出并保存在request域中,然后在jsp页面,取出request域中存放的数据!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EMrdttv5-1644134869545)(C:/Users/12709/AppData/Roaming/Typora/typora-user-images/image-20211119183039833.png)]

要使得jsp页面获取全部图书数据,必须要从管理页面中进入jsp页面,也就是book_manager页面,进入该页面后,会访问BookServlet程序!在进行这一系列操作之前,需要将JSTL所需要的jar包进行导入!

<!-- Servlet-JSP所需的jar包-->
<!-- https://mvnrepository.com/artifact/jakarta.servlet.jsp/jakarta.servlet.jsp-api -->
<dependency>
  <groupId>jakarta.servlet.jsp</groupId>
  <artifactId>jakarta.servlet.jsp-api</artifactId>
  <version>3.0.0</version>
  <scope>provided</scope>
</dependency>

<!-- Servlet-JSP-JSTL所需的jar包-->
<dependency>
  <groupId>org.apache.taglibs</groupId>
  <artifactId>taglibs-standard-spec</artifactId>
  <version>1.2.5</version>
</dependency>
<dependency>
  <groupId>org.apache.taglibs</groupId>
  <artifactId>taglibs-standard-impl</artifactId>
  <version>1.2.5</version>
</dependency>

web.xml中注册BookServlet程序:

  <servlet>
    <servlet-name>BookServlet</servlet-name>
    <servlet-class>com.atayin.web.BookServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>BookServlet</servlet-name>
    <url-pattern>/manager/bookServlet</url-pattern>
  </servlet-mapping>
</web-app>

注意:这里注册的路径并不是普通的/bookServlet,而是多了前面的/manager,原因是前台和后台的权限需要分开,当路径出现manager的时候,就代表着进入了后台管理页面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JUhyXlDA-1644134869546)(C:/Users/12709/AppData/Roaming/Typora/typora-user-images/image-20211119223515490.png)]

manager_menu中修改跳转路径:

<a href="manager/bookServlet?action=list">图书管理</a>

这行代码会跳转至manager/bookServlet路径并调用BookServlet程序,发送action,并寻找list方法!

注意:a标签的发起的请求是get请求,因为之前在BaseServlet中只写了doPost方法,所以需要在BaseServlet中需要加上doGet方法来捕获get请求,不然页面会报405错误!解决方法也非常简单,仅需要在方法体中调用doPost方法即可!

// BaseServlet程序
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doPost(req, resp);
}

BookServlet的list方法:

// 遍历所有图书
protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 通过bookService查询所有图书
    List<Book> books = bookService.queryBooks();
    // 将所有图书保存在request域中
    req.setAttribute("books", books);
    // 请求转发到pages/manager/book_manager.jsp页面
    req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req, resp);
}

使用JSTL标签库遍历图书:

<c:forEach items="${requestScope.books}" var="book">
   <tr>
      <td>${book.name}</td>
      <td>${book.price}</td>
      <td>${book.author}</td>
      <td>${book.sales}</td>
      <td>${book.stock}</td>
      <td><a href="pages/manager/book_edit.jsp">修改</a></td>
      <td><a href="#">删除</a></td>
   </tr>
</c:forEach>

执行以上步骤有可能会发生以下几种问题:

  • 点击图书管理后,跳转的页面为一片空白,并在控制台中报无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]的错误

解决方法:降低Tomcat版本或者再检查jar包的导入是否完整,所需的jar包:jstl和standard!如果还是报错,则找到lib目录下jakarta.servlet.jsp.jstl-2.0.0的jar包文件,将其解压,并在META-INF目录下复制所有的*.tld文件,粘贴在WEB-INF目录之下!

  • 跳转页面报错:jakarta.servlet.ServletException: 类com.seig.servlet.HelloServlet不是Servlet

解决方法:降低Tomcat版本或者检查Tomcat版本是否为10以上的版本,Tomcat10以上的版本所需的servlet的jar包已经变更为jakarta.servlet!

<!-- Servlet-JSP所需的jar包-->
<!-- https://mvnrepository.com/artifact/jakarta.servlet.jsp/jakarta.servlet.jsp-api -->
<dependency>
    <groupId>jakarta.servlet.jsp</groupId>
    <artifactId>jakarta.servlet.jsp-api</artifactId>
    <version>3.0.0</version>
    <scope>provided</scope>
</dependency>
  • 控制台警告信息:org.apache.tomcat.util.descriptor.web.WebXml.setVersion Unknown version string [4.0]

解决方法:在web.xml中修改版本为3.1,或者提高Tomcat版本为9以上!

  • 控制台警告信息:至少有一个JAR被扫描用于TLD但尚未包含TLD

解决方法:找到 Tomcat 服务器安装目录下的 conf 文件夹下的catalina.properties文件,如D:\code\apache-tomcat-9.0.24\conf\catalina.properties,找到下面这一行,并编辑配置文件:

tomcat.util.scan.StandardJarScanFilter.jarsToSkip=/

将 / 改为 *.jar

tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar

然后保存并重启服务器即可!

添加图书的实现

在实现该功能之前,需要修改页面中尚未修改的路径!

在图书管理页面中,点击”添加图书“会进入编辑添加图书的页面,并在这个页面中填写需要添加的图书信息,并提交表单至BookServlet程序,程序会获取图书信息并封装成Book对象,调用BookService层中的添加图书方法addBook,然后重定向页面回到图书管理页面中,并刷新页面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyK0b7qu-1644134869546)(C:/Users/12709/AppData/Roaming/Typora/typora-user-images/image-20211119230304643.png)]

为了获取表单提交的信息,需要在表单内定义一个隐藏域:

<form action="manager/bookServlet" method="get">
   <input type="hidden" name="action" value="add">
   <table>
      <tr>
         <td>名称</td>
         <td>价格</td>
         <td>作者</td>
         <td>销量</td>
         <td>库存</td>
         <td colspan="2">操作</td>
      </tr>     
      <tr>
         <td><input name="name" type="text" value="时间简史"/></td>
         <td><input name="price" type="text" value="30.00"/></td>
         <td><input name="author" type="text" value="霍金"/></td>
         <td><input name="sales" type="text" value="200"/></td>
         <td><input name="stock" type="text" value="300"/></td>
         <td><input type="submit" value="提交"/></td>
      </tr>  
   </table>
</form>

需要注意的是,input标签中,name属性的值需要和Book对象中定义的变量一致!在表单提交时,会发起get请求,并进入BookServlet程序,并查找为add方法并执行:

protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取请求的参数并封装成Book对象
    Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());
    // bookService调动addBook()方法添加图书
    bookService.addBook(book);
    // 页面重定向
    resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

获取请求的操作可以通过之前定义的WebUtils工具类来获取,工具类会将数据封装成Book对象。最后使用resp的重定向,再次回到BookServlet程序中,并执行list方法刷新页面数据。

**注意:**在这里,页面的跳转使用的是resp的重定向而不是req的请求转发,因为请求转发为发起一次请求,而现在的浏览器会保存最后一次发起的请求,当用户使用功能键F5刷新页面时,浏览器会再次发起这次请求,也就是再执行一次add方法,这就产生了表格重复提交的问题;而重定向会发起两次请求,当用户刷新页面时,浏览器最后执行的请求是执行list方法!

删除图书的实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uecczSVn-1644134869547)(C:/Users/12709/AppData/Roaming/Typora/typora-user-images/image-20211120002008083.png)]

删除图书的功能实现流程大致与添加图书相同,具体代码实例如下:

在book_manager中,点击“删除”链接后跳转至BookServlet的delete业务方法:

<c:forEach items="${requestScope.books}" var="book">
   <tr>
      <td>${book.name}</td>
      <td>${book.price}</td>
      <td>${book.author}</td>
      <td>${book.sales}</td>
      <td>${book.stock}</td>
      <td><a href="#">修改</a></td>
      <td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
   </tr>
</c:forEach>

delete业务方法:

protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取id并调用工具类进行转型
    int id = WebUtils.parseInt(req.getParameter("id"), 0);
    // bookService调动deleteBook()方法添加图书
    bookService.deleteBookById(id);
    // 页面重定向
    resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

需要注意的是,因为在Book类中,id的类型是Integer类型,而bookService根据指定id删除图书deleteBookById()方法需要的参数是int类型的,所以需要进行转型,在接下来的功能实现中,需要经常使用到转型操作,所以可以将转型操作写在了WebUtils工具类中!

/**
 * Integer转型
 * @param strIng 需要进行转型的字符串
 * @param defaultValue 默认值
 * @return 如果转型失败,则返回默认值
 */
public static int parseInt(String strIng, int defaultValue) {
    try{
        return Integer.parseInt(strIng);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return defaultValue;
}

按照如上操作,就可以顺利实现图书的删除功能,但是在用户使用的过程中,可能会发生不小心点击了删除链接而误将不想删除的图书进行删除操作,为了防止这种情况的发生,可以在页面中做一个弹窗确认!

<%-- book_manager.jsp --%>
<script>
   $(function () {
      $("a.deleteClass").click(function () {
         return confirm("你确定要删除《" + $(this).parent().parent().find("td:first").text() + "》吗?");
      })
   })
</script>

在JQuery中,confirm方法可以弹出一个确认弹窗,有“确定”和“取消”两个选项,点击后分别返回“true”和“false”,而页面返回false时,可以阻止标签的默认行为,也就是会阻止表单的提交行为。

注意:this关键字代表着当前正在响应事件的 dom 对象!

修改图书的实现

当用户点击“修改”时,会跳转至book_edit页面,页面需要显示用户修改的图书信息,用户再次点击提交之后,返回book_manager页面,并刷新页面数据,将修改的图书信息展现出来。

以上操作需要分为两个步骤去实现:

  • 将修改的图书信息进行回显
  • 提交修改之后的数据给服务器进行保存
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p9ivdbl2-1644134953557)(C:/Users/12709/AppData/Roaming/Typora/typora-user-images/image-20211120171503996.png)]

在book_manager中,点击“修改”链接后跳转至BookServlet的getBook业务方法:

<c:forEach items="${requestScope.books}" var="book">
    <tr>
        <td>${book.name}</td>
        <td>${book.price}</td>
        <td>${book.author}</td>
        <td>${book.sales}</td>
        <td>${book.stock}</td>
        <td><a href="manager/bookServlet?action=getBook&id=${book.id}">修改</a></td>
        <td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
    </tr>
</c:forEach>

getBook业务方法:

protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取请求的参数图书编号
    int id = WebUtils.parseInt(req.getParameter("id"), 0);
    // 调用bookService.queryBookById查询图书
    Book book = bookService.queryBookById(id);
    // 将图书信息保存在request域中
    req.setAttribute("book", book);
    // 请求转发至book_edit.jsp页面
    req.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(req, resp);
}

在book_edit页面中设置图书信息的回显:

<div id="main">
   <form action="manager/bookServlet" method="get">
      <input type="hidden" name="action" value="add">
      <table>
         <tr>
            <td>名称</td>
            <td>价格</td>
            <td>作者</td>
            <td>销量</td>
            <td>库存</td>
            <td colspan="2">操作</td>
         </tr>
            <tr>
               <td><input name="name" type="text" value="${requestScope.book.name}"/></td>
               <td><input name="price" type="text" value="${requestScope.book.price}"/></td>
               <td><input name="author" type="text" value="${requestScope.book.author}"/></td>
               <td><input name="sales" type="text" value="${requestScope.book.sales}"/></td>
               <td><input name="stock" type="text" value="${requestScope.book.stock}"/></td>
               <td><input type="submit" value="提交"/></td>
            </tr>
      </table>
   </form>

但是在这里产生了一个问题:修改图书和添加图书都会进入book_edit页面,也就是说,表单需要根据不同的需求,而变更隐藏域中的value值!

解决方法一共有三种:

  • 在book_manager中,”修改“和“添加”的链接增添新属性method,并将method属性的值分别设置为update和add,并在book_edit页面中接受method的值,实现动态修改隐藏域的value!
<td><a href="manager/bookServlet?action=getBook&id=${book.id}&method=update">修改</a></td>
<td><a href="pages/manager/book_edit.jsp?method=add">添加图书</a></td>
<input type="hidden" name="action" value="${param.method}">
  • 因为修改操作的请求参数中包含着id参数,可以在book_edit使用EL表达式判断当前请求参数是否有id参数,如果有则执行修改操作,如果没有则执行添加操作!
<input type="hidden" name="action" value="${empty param.id ? "add" : "update"}">
  • 因为修改操作会在request域中传入一个book对象,可以使用EL表达式判断当前request域中是否有book对象,如果有则执行修改操作,如果没有则执行添加操作!
<input type="hidden" name="action" value="${empty requestScope.book ? "add" : "update"}">

选择一种解决方法,就可以解决变更隐藏域的需求,但是,修改图书的需求看似已经实现了,但是实际运行起来,发现图书修改之后,更行的页面并没有显示修改后的图书!

通过Debug发现,当图书进入bookService方法时,提交的book对象是没有id参数的,而在Dao层中,修改数据库表字段的方法是需要id参数的!

也就是说,表单提交时,需要提交一个id参数!这是request域中正好保存着需要修改的图书,可以通过在表单下添加一个隐藏域的方法,将获取id参数并提交!

<input type="hidden" name="id" value="${requestScope.book.id}">

这样一来,修改图书的需求就基本实现了!

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值