经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Servlet » 查看文章
使用“纯”Servlet做一个单表的CRUD操作 - Rainbow-Sea
来源:cnblogs  作者:Rainbow-Sea  时间:2023/4/12 11:17:08  对本文有异议

使用“纯”Servlet做一个单表的CRUD操作

在这里插入图片描述

每博一文案

  1. 庄子说:"独往独来,是谓独有。独有之人,是谓至贵"。热闹是别人的狂欢,而孤独是自己的自由。
  2. 相聚总是短暂,唯孤独绵长,学会孤独为伍,才能找到浮世清欢。
  3. 金庸曾笔下人物说:"天上的白云聚了又聚,散了又散,人生离合,亦复如斯。" 人生一世,所有的人来人往,
  4. 聚散离合,都是缘分使然。缘来便聚,缘尽则散,聚是一团火,散是满天星。往后余生,与孤独和解,做自己的知音。
  5. 都说幸福的样子千篇一律,不幸的人却各有各的不幸。很多人都只能看到别人所拥有的,却没看到人所失去的。
  6. 没有人的生活是真正的容易。当你站在桥上看风景的时候,可能自己亦是别人眼中的风景。
  7. 所以不必羡慕别人,珍惜现在所拥有的,过好自己的人生才最重要。
  8. —————— 《一禅心灵庙语》

@

1. 项目说明

介绍: 这里我们使用 纯粹Servlet 完成单表【对部门的】的增删改查操作。(B/S结构的。)

结构图

在这里插入图片描述

初始的欢迎页面

在这里插入图片描述

部门列表页面

在这里插入图片描述

部门详情

在这里插入图片描述

修改部门
在这里插入图片描述

删除部门:

在这里插入图片描述

新增部门:

在这里插入图片描述

2. 具体对应的功能的代码实现

2.1 准备工作:

  1. 首先我们使用数据库,存储数据,这里我们使用的数据库是 MYSQL
  2. 我们需要准备一个名为 dept的数据表,并插入一些数据。
  1. create table dept (
  2. depton int primary key,
  3. dname varchar(255),
  4. loc varchar(255)
  5. );
  6. insert into dept(depton,dname,loc) values(10,'xiaoShouBu','BEIJING');
  7. insert into dept(depton,dname,loc) values(20,'YanFaBu','SHANGHAI');
  8. insert into dept(depton,dname,loc) values(30,'JisShuBu','GUANGZHOU');
  9. insert into dept(depton,dname,loc) values(40,'MeiTiBu','SHENZHEN');
  10. select * from dept;

小技巧: MySQL 在 cmd 命令中,批量执行 sql语句的方法:如下,首先将编写好的 .sql 文件存储起来。如下图所示,
在这里插入图片描述

再打开cmd 进入命令窗口,再进入到Mysql当中,输入如下命令:

  1. source 后接文件路径(要执行的批量的.sql文件)

在这里插入图片描述

当前数据表 dept 的信息内容如下:

在这里插入图片描述

  1. 为该模块导入 MYSQL的 JDBC 的 jar 包。

注意: 因为我们是在 Tomcat 服务器当中部署项目的,所以我们需要在 WEB-INF 的目录下,创建一个名为 lib 的目录文件夹,用来存放相关的 依赖jar 包,注意路径位置不可以修改,目录文件必须为 lib 不然,当你启动的Tocmat 服务器的时候,是无法找到该对应的 依赖jar 包的。具体如下,我们将 Mysql对应的 jdbc jar 包导入其中。

  • 创建一个webapp(给这个webapp添加servlet-api.jar和jsp-api.jar到classpath当中。)
  • 向webapp中添加连接数据库的jar包(mysql驱动)
    • 必须在WEB-INF目录下新建lib目录,然后将mysql的驱动jar包拷贝到这个lib目录下。这个目录名必须叫做lib,全部小写的。

在这里插入图片描述

2.2 模块目录结构

在这里插入图片描述

2.3 工具类 DBUtil

这里因为我们要连接数据库,所以我们编写一个连接Mysql 数据库的 工具类,这里我们名为一个 DBUtil 的工具类。

这里我们通过读取配置jdbc.properties的配置文件的方式,注册相对应的Mysql驱动

如下是相关: jdbc.properties 的配置信息

  1. driver=com.mysql.cj.jdbc.Driver
  2. url=jdbc:mysql://localhost:3306/test
  3. user=root
  4. password=123

再编写好相关的:DBUtil 类 ,具体代码的编写内容如下:

  1. package com.RainbowSea.DBUtil;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. import java.util.ResourceBundle;
  8. /**
  9. * JDBC工具类
  10. */
  11. public class DBUtil {
  12. // 静态变量,在类加载时执行
  13. // 都是一些从 jdbc.properties 读取到的配置文件的信息
  14. // 该方法仅仅只会读取 “.properties" 的后缀的文件,注意:默认是从src目录开始的,有子目录需要写明子目录
  15. private static ResourceBundle bundle = ResourceBundle.getBundle("com/RainbowSea/resources/jdbc");
  16. private static String driver = bundle.getString("driver"); // 根据properties中的name读取对应的value值
  17. private static String url = bundle.getString("url");
  18. private static String user = bundle.getString("user");
  19. private static String password = bundle.getString("password");
  20. static {
  21. // 注册驱动(注册驱动只需要注册一次,放在静态代码当中,DBUtil类加载的时候执行)
  22. // "com.mysql.jdbc.Driver"是连接数据库的驱动,不能写死,因为以后可能还会连接Oracle数据库。
  23. // OCP开闭原则: 对扩展开放,对修改关闭(什么是符合 OCP呢?在进行功能扩展的时候,不需要修改java源代码)
  24. // Class.forName("com.mysql.jdbc.Driver")
  25. try {
  26. Class.forName(driver); // 加载驱动
  27. } catch (ClassNotFoundException e) {
  28. throw new RuntimeException(e);
  29. }
  30. }
  31. /**
  32. * 获取数据库连接
  33. * @return Connection
  34. */
  35. public static Connection getConnection() throws SQLException {
  36. return DriverManager.getConnection(url,user,password);
  37. }
  38. /**
  39. * 关闭连接
  40. * @param connection
  41. * @param statement
  42. * @param resultSet
  43. */
  44. public static void close(Connection connection, Statement statement, ResultSet resultSet) {
  45. // 注意:分开try,最后使用的资源,优先关闭
  46. if(resultSet != null) {
  47. try {
  48. resultSet.close();
  49. } catch (SQLException e) {
  50. throw new RuntimeException(e);
  51. }
  52. }
  53. if(statement != null) {
  54. try {
  55. statement.close();
  56. } catch (SQLException e) {
  57. throw new RuntimeException(e);
  58. }
  59. }
  60. if(connection != null) {
  61. try {
  62. connection.close();
  63. } catch (SQLException e) {
  64. throw new RuntimeException(e);
  65. }
  66. }
  67. }
  68. }

2.4 功能一: index.html 该项目的欢迎页面如下:

默认在 web 当中,一个全局配置信息当中,会将名为 index.html 的文件,设置为该项目的欢迎页面。相应的具体内容大家可以移步至 ?????? 关于Web的欢迎页面的开发设置_ChinaRainbowSea的博客-CSDN博客

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>欢迎使用OA系统</title>
  6. </head>
  7. <body>
  8. <!--注意:对应前端的资源获取基本上都是要加项目名的,并且要"/"开始-->
  9. <a href="/servlet09/dept/list/">查看部门列表</a>
  10. </body>
  11. </html>

2.5 功能二:部门列表 DeptListServlet

注意:因为我们这里使用的是 纯 Servlet 编写的一个项目,所以在后端想要将相关的 HTML 标签相应到前端浏览器,被浏览器渲染的话,则需要特殊的方法:如下

  1. // 设置将后端的字符串的 html 标签相应到浏览器端执行处理,并设置相应的字符集编码
  2. response.setContentType("text/html;charSet=UTF-8");
  3. PrintWriter writer = response.getWriter();

思路:

  1. DeptListServlet类的doGet方法中连接数据库,查询所有的部门,动态的展示部门列表页面.
  • 分析 html 页面中哪部分是固定死的,哪部分是需要动态展示的。

  • html页面中的内容所有的双引号要替换成单引号,因为out.print("")这里有一个双引号,容易冲突。

  • 现在写完这个功能之后,你会有一种感觉,感觉开发很繁琐,只使用servlet写代码太繁琐了

  1. 我们需要连接数据库,从数据库中获取到数据,显示到前端浏览器当中。
  2. 注意我们这里上面的 index.html 中是通过超链接的方式,跳转到该 部门列表页面的。超链接是 doGet 请求。
  1. package com.RainbowSea.servlet;
  2. import com.RainbowSea.DBUtil.DBUtil;
  3. import jakarta.servlet.ServletException;
  4. import jakarta.servlet.http.HttpServlet;
  5. import jakarta.servlet.http.HttpServletRequest;
  6. import jakarta.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.io.PrintWriter;
  9. import java.sql.Connection;
  10. import java.sql.PreparedStatement;
  11. import java.sql.ResultSet;
  12. import java.sql.SQLException;
  13. /**
  14. * 部门列表
  15. */
  16. public class DeptListServlet extends HttpServlet {
  17. /*
  18. 说明:这里使用了doGet,和 goPost 的原因是,我们前端的 DeptSaveServlet 的新增部门,
  19. 的请求是doPost,从 doPost 请求 "转发"出来的同样是 doPost请求的,而 重定向就是doGet请求了,无论是doPost,doGet请求都是
  20. 所以这里为了,处理接受到 DeptSaveServlet 的新增部门的 "转发"请求,写了一个doPost 请求处理
  21. */
  22. // 优化,将转发,替换成了 重定向的机制,(重定向的机制)是自发到浏览器前端的地址栏上的,后自发的执行
  23. // 地址栏上是 doGet 请求的,就不需要 doPost 请求了。
  24. /* @Override
  25. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  26. IOException {
  27. doGet(request, response); // 调用本身这里的doGet()请求
  28. }*/
  29. /*
  30. 因为我们前端使用的是 <a>超链接,是goGet请求所以,
  31. 前后端的请求保持一致。
  32. */
  33. @Override
  34. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  35. IOException {
  36. // 设置前端浏览器显示的格式类型,以及编码
  37. response.setContentType("text/html;charSet=UTF-8");
  38. PrintWriter writer = response.getWriter();
  39. Connection connection = null;
  40. PreparedStatement preparedStatement = null;
  41. ResultSet resultSet = null;
  42. // 获取到该webapp的项目根路径:也就是在Tomcat 当中设置的访问的项目路径
  43. // 注意的是: getContextPath()获取返回的路径是带有 "/项目名"的,所以不要多写了 /
  44. String contextPath = request.getContextPath();
  45. int i = 0;
  46. writer.println(" <!DOCTYPE html>");
  47. writer.println("<html lang='en'>");
  48. writer.println("<head>");
  49. writer.println(" <meta charset='UTF-8'>");
  50. writer.println(" <title>部门列表页面</title>");
  51. writer.println("</head>");
  52. writer.println(" <script type = 'text/javascript' >");
  53. writer.println(" function del(dno) {");
  54. // 弹出确认框,用户点击确定,返回true,点击取消返回false
  55. writer.println(" var ok = window.confirm('亲,删了不可恢复哦!');");
  56. writer.println(" if (ok) {");
  57. // 发送请求进行删除数据的操作
  58. // 在js代码当中如何发送请求给服务
  59. // document.location.href='请求路径
  60. // document.location = '请求路径'")
  61. // window.location.href = '请求路径
  62. // window.location = '请求路径'
  63. // 注意是根据所传的部门编号删除数据的
  64. writer.println(" document.location.href = '" + contextPath + "/dept/delete?deptno=' + dno");
  65. writer.println(" }");
  66. writer.println(" }");
  67. writer.println("</script >");
  68. writer.println("<body>");
  69. writer.println(" <h1 align='center'>部门列表</h1>");
  70. writer.println(" <table border='1px' align='center' width='50%'>");
  71. writer.println(" <tr>");
  72. writer.println(" <th>序号</th>");
  73. writer.println(" <th>部门编号</th>");
  74. writer.println(" <th>部门名称</th>");
  75. writer.println(" </tr>");
  76. try {
  77. // 连接数据库,查询所有部门:
  78. // 1. 注册驱动,获取连接
  79. connection = DBUtil.getConnection();
  80. // 2. 获取操作数据库对象,预编译sql语句
  81. String sql = "select depton as det,dname,loc from dept"; // 在mysql中测试一下是否正确
  82. preparedStatement = connection.prepareStatement(sql);
  83. // 3. 执行sql语句
  84. resultSet = preparedStatement.executeQuery();
  85. // 4. 处理查询结果集
  86. while (resultSet.next()) {
  87. String det = resultSet.getString("det"); // 有别名要使用别名
  88. String dname = resultSet.getString("dname");
  89. String loc = resultSet.getString("loc");
  90. writer.print(" <tr>");
  91. writer.print(" <td>" + (++i) + "</td>");
  92. writer.print(" <td>" + det + "</td>");
  93. writer.print(" <td>" + dname + "</td>");
  94. writer.print(" <td>");
  95. writer.print(" <a href='javascript:void(0)' onclick= 'del(" + det + ")'>删除</a>");
  96. // 将部门编号传过去,用户数据库查询修改
  97. writer.print(" <a href='"+contextPath+"/dept/edit?deptno="+det+"'>修改</a>");
  98. //注意这里的是前端的资源,需要加项目名,但是这里的项目名我们通过 getContestPath()方法动态获取
  99. // 并且将部门名传过去,再从数据库当中查找出来对应的部门的详细信息:注意: ?(间隔) Http传输协议
  100. writer.print(" <a href='" + contextPath + "/dept/detail?deptno=" + det + "'>详情</a>");
  101. writer.print(" </td>");
  102. writer.print(" </tr>");
  103. }
  104. } catch (SQLException e) {
  105. throw new RuntimeException(e);
  106. } finally {
  107. // 5. 关闭资源
  108. DBUtil.close(connection, preparedStatement, resultSet);
  109. }
  110. writer.println("</table>");
  111. writer.println("<hr>");
  112. // 前端的资源路径访问需要加项目名
  113. writer.println("<a href='" + contextPath + "/add.html'>新增部门</a>");
  114. writer.println("</body>");
  115. writer.println("</html>");
  116. }
  117. }

2.6 功能三:查看部门详情 DeptDetailServlet

建议:从前端往后端一步一步实现。首先要考虑的是,用户点击的是什么?用户点击的东西在哪里?

一定要先找到用户点的“详情”在哪里。这里我们使用超链接的形式处理

  1. <a href='写一个路径'>详情</a>

详情 是需要连接数据库的,所以这个超链接点击之后也是需要执行一段java代码的。所以要将这个超链接的路径修改一下。

注意:修改路径之后,这个路径是需要加项目名的。"/servlet09/dept/detail"

技巧:

  1. out.print("<a href='"+contextPath+"/dept/detail?deptno="+deptno+"'>详情</a>");

重点:向服务器提交数据的格式:uri?name=value&name=value&name=value&name=value 。这里的问号,必须是英文的问号。不能中文的问号。

思路:

中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
第一步:获取部门编号
第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
第三步:将部门信息响应到浏览器上。(显示一个详情。)

  1. package com.RainbowSea.servlet;
  2. import com.RainbowSea.DBUtil.DBUtil;
  3. import jakarta.servlet.ServletException;
  4. import jakarta.servlet.http.HttpServlet;
  5. import jakarta.servlet.http.HttpServletRequest;
  6. import jakarta.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.io.PrintWriter;
  9. import java.sql.Connection;
  10. import java.sql.PreparedStatement;
  11. import java.sql.ResultSet;
  12. import java.sql.SQLException;
  13. /**
  14. * 部门详情
  15. */
  16. public class DeptDetailServlet extends HttpServlet {
  17. // 前端超连接是 doGet()请求
  18. @Override
  19. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  20. IOException {
  21. // 设置前端浏览器格式类型和字符集编码
  22. response.setContentType("text/html;charSet=UTF-8");
  23. PrintWriter writer = response.getWriter();
  24. Connection connection = null;
  25. PreparedStatement preparedStatement = null;
  26. ResultSet resultSet = null;
  27. //中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
  28. // 第一步:获取部门编号
  29. // 第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
  30. // 第三步:将部门信息响应到浏览器上。(显示一个详情。)
  31. // 1.
  32. // http://127.0.0.1:8080/servlet09/dept/detail?deptno=40
  33. String deptno = request.getParameter("deptno"); // 注意是我们前端提交的数据,建议复制name
  34. try {
  35. // 2. 连接数据库,根据部门编号查询数据库
  36. // 1.注册驱动,连接数据库
  37. connection = DBUtil.getConnection();
  38. // 2. 预编译SQL语句,sql要测试
  39. String sql = "select dname,loc from dept where depton = ?"; // ? 占位符
  40. preparedStatement = connection.prepareStatement(sql);
  41. // 3. 填充占位符,真正执行sql语句
  42. preparedStatement.setString(1, deptno);
  43. resultSet = preparedStatement.executeQuery();
  44. // 4. 处理查询结果集
  45. while (resultSet.next()) {
  46. String dname = resultSet.getString("dname");
  47. String loc = resultSet.getString("loc");
  48. // 注意将 “双引号转换为单引号,因为在Java当中不可以嵌套多个双引号,除非是字符串的拼接
  49. // 所以使用 '单引号
  50. writer.println(" <body>");
  51. writer.println(" <h1>部门详情</h1>");
  52. writer.println(" 部门编号: " + deptno + " <br>");
  53. writer.println(" 部门名称: " + dname + "<br>");
  54. writer.println(" 部门位置: " + loc + "<br>");
  55. writer.println(" <input type='button' value='后退' onclick='window.history.back()' />");
  56. }
  57. } catch (SQLException e) {
  58. throw new RuntimeException(e);
  59. } finally {
  60. // 5. 释放资源
  61. DBUtil.close(connection, preparedStatement, resultSet);
  62. }
  63. writer.println("</body>");
  64. writer.println("</html>");
  65. }
  66. }

2.7 功能四:删除部门 DeptDelServlet

怎么开始?从哪里开始?从前端页面开始,用户点击删除按钮的时候,应该提示用户是否删除。因为删除这个动作是比较危险的。任何系统在进行删除操作之前,是必须要提示用户的,因为这个删除的动作有可能是用户误操作。(在前端页面上写JS代码,来提示用户是否删除。)

  1. <a href="javascript:void(0)" onclick="del(30)" >删除</a>
  2. <script type="text/javascript">
  3. function del(dno){
  4. if(window.confirm("亲,删了不可恢复哦!")){
  5. document.location.href = "/oa/dept/delete?deptno=" + dno;
  6. }
  7. }
  8. </script>

以上的前端程序要写到后端的java代码当中:DeptListServlet类的doGet方法当中,使用out.print()方法,将以上的前端代码输出到浏览器上。

删除成功或者失败的时候的一个处理(这里我们一开始使用的选择的是转发,后面优化使用的是重定向机制。)

删除成功:我们跳转到部门列表当中。DeptListServlet

删除失败:我们跳转到一个失败的页面当中。这里我们将该失败的页面名为: error.html 页面如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>error</title>
  6. </head>
  7. <body>
  8. <h1> 操作失败: </h1>
  9. <a href='/servlet09/dept/list/' onclick="window.history.back()">返回</a>
  10. </body>
  11. </html>

具体的 Servlet 编写如下

  1. package com.RainbowSea.servlet;
  2. import com.RainbowSea.DBUtil.DBUtil;
  3. import jakarta.servlet.ServletException;
  4. import jakarta.servlet.http.HttpServlet;
  5. import jakarta.servlet.http.HttpServletRequest;
  6. import jakarta.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.io.PrintWriter;
  9. import java.sql.Connection;
  10. import java.sql.PreparedStatement;
  11. import java.sql.SQLException;
  12. /**
  13. * 删除部门
  14. */
  15. public class DeptDelServlet extends HttpServlet {
  16. /*
  17. 注意前端是超链接的方式:是get请求
  18. */
  19. @Override
  20. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  21. IOException {
  22. response.setContentType("text/html;charSet=UTF-8");
  23. PrintWriter writer = response.getWriter();
  24. request.setCharacterEncoding("UTF-8");
  25. // 思路:
  26. /*
  27. 根据部门编号删除信息,
  28. 删除成功,跳转回原来的部门列表页面
  29. 删除失败,跳转删除失败的页面
  30. */
  31. Connection connection = null;
  32. PreparedStatement preparedStatement = null;
  33. // 记录删除数据库的行数
  34. int count = 0;
  35. // 获取到前端提交的数据
  36. String deptno = request.getParameter("deptno");
  37. // 连接数据库进行删除操作
  38. try {
  39. // 1.注册驱动,连接数据库
  40. connection = DBUtil.getConnection();
  41. // 开启事务(取消自动提交机制),实现可回滚
  42. connection.setAutoCommit(false);
  43. // 2. 预编译sql语句,sql测试
  44. String sql = "delete from dept where depton = ?"; // ? 占位符
  45. preparedStatement = connection.prepareStatement(sql);
  46. // 3. 填充占位符,真正的执行sql语句
  47. preparedStatement.setString(1, deptno);
  48. // 返回影响数据库的行数
  49. count = preparedStatement.executeUpdate();
  50. connection.commit(); // 手动提交数据
  51. } catch (SQLException e) {
  52. // 遇到异常回滚
  53. if (connection != null) {
  54. try {
  55. // 事务的回滚
  56. connection.rollback();
  57. } catch (SQLException ex) {
  58. throw new RuntimeException(ex);
  59. }
  60. }
  61. throw new RuntimeException(e);
  62. } finally {
  63. // 4. 释放资源
  64. // 因为这里是删除数据,没有查询操作,所以 没有 ResultSet 可以传null
  65. DBUtil.close(connection, preparedStatement, null);
  66. }
  67. if (count == 1) {
  68. // 删除成功
  69. // 仍然跳转到部门列表页面
  70. // 部门列表页面的显示需要执行另外一个Servlet,怎么办,可以使用跳转,不过这里最后是使用重定向
  71. // 注意:转发是在服务器间的,所以不要加“项目名” 而是 / + web.xml 映射的路径即可
  72. //request.getRequestDispatcher("/dept/list/").forward(request,response);
  73. // 优化:使用重定向机制 注意: 重定向是自发到前端的地址栏上的,前端所以需要指明项目名
  74. // 注意: request.getContextPath() 返回的根路径是,包含了 "/" 的
  75. response.sendRedirect(request.getContextPath() + "/dept/list/");
  76. } else {
  77. // 删除失败
  78. // web当中的 html资源,这里的 "/" 表示 web 目录
  79. //request.getRequestDispatcher("/error.html/").forward(request, response);
  80. // 优化,使用重定向
  81. response.sendRedirect(request.getContextPath() + "/error.html/");
  82. }
  83. }
  84. }

2.8 功能五:新增部门 DeptSaveServlet

思路:

获取到前端 form 表单提交的数据,这里我们 form 表单 中的 metod = post 设置为了 doPost 请求。

再连接数据库,添加一条记录。

添加成功:我们跳转到部门列表当中。DeptListServlet

添加失败:我们跳转到一个失败的页面当中。这里我们将该失败的页面名为: error.html

注意点:

最后保存成功之后,跳转到 /dept/list 的时候,如果你使用的是 转发 机制的话,这里因为你是从 doPost 请求 转发过去的(转发是一次请求,之前是post,之后还是post,因为它是一次请求。),所以对应接收该 doPost 请求的也要是 doPost 方法处理该请求,不然会报 405 错误。

而同时:这时候接收该 转发 的 /dept/list Servlet当中只有一个doGet方法。就会报 405 错误。

怎么解决?两种方案

  • 第一种:在/dept/list Servlet中添加doPost方法,然后在doPost方法中调用doGet。
  • 第二种:使用重定向的方式,进行跳转,重定向的机制是改变浏览器的请求路径URL,让浏览器重新发送跳转之后的 URL 地址,该方式是从浏览器地址栏上跳转的,所以是 doGet 请求,就不要编写 doPost 请求了。

具体代码编写如下:

  1. package com.RainbowSea.servlet;
  2. import com.RainbowSea.DBUtil.DBUtil;
  3. import jakarta.servlet.ServletException;
  4. import jakarta.servlet.http.HttpServlet;
  5. import jakarta.servlet.http.HttpServletRequest;
  6. import jakarta.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.sql.Connection;
  9. import java.sql.PreparedStatement;
  10. import java.sql.SQLException;
  11. /**
  12. * 增加部门数据
  13. */
  14. public class DeptSaveServlet extends HttpServlet {
  15. // 前端是注册信息,是post 请求
  16. @Override
  17. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  18. IOException {
  19. /*
  20. 思路:
  21. 获取到前端的提交的数据,注意 编码设置post 请求
  22. 连接数据库: 进行添加数据
  23. 添加成功: 返回部门列表页面
  24. 添加失败: 返回失败的页面
  25. */
  26. request.setCharacterEncoding("UTF-8");
  27. // 获取到前端的数据,建议 name 使用复制
  28. String deptno = request.getParameter("deptno");
  29. String dname = request.getParameter("dname");
  30. String loc = request.getParameter("loc");
  31. // 连接数据库,添加数据
  32. Connection connection = null;
  33. PreparedStatement preparedStatement = null;
  34. // 影响数据库的行数
  35. int count = 0;
  36. try {
  37. // 1. 注册驱动,连接数据库
  38. connection = DBUtil.getConnection();
  39. // 2. 获取操作数据库对象,预编译sql语句,Sql测试
  40. String sql = "insert into dept(depton,dname,loc) values(?,?,?)";
  41. preparedStatement = connection.prepareStatement(sql);
  42. // 3. 填充占位符, 真正执行sql语句,
  43. // 注意: 占位符的填充是从 1 开始的,基本上数据库相关的起始下标索引都是从 1下标开始的
  44. preparedStatement.setString(1, deptno);
  45. preparedStatement.setString(2, dname);
  46. preparedStatement.setString(3, loc);
  47. // 返回影响数据库的行数
  48. count = preparedStatement.executeUpdate();
  49. // 5.释放资源
  50. } catch (SQLException e) {
  51. throw new RuntimeException(e);
  52. } finally {
  53. DBUtil.close(connection, preparedStatement, null);
  54. }
  55. // 保存成功,返回部门列表页面
  56. if (count == 1) {
  57. // 这里应该使用,重定向
  58. // 这里用的转发,是服务器内部的,不要加项目名
  59. //request.getRequestDispatcher("/dept/list/").forward(request, response);
  60. // 重定向
  61. response.sendRedirect(request.getContextPath() + "/dept/list/");
  62. } else {
  63. // 保存失败
  64. // web当中的 html资源,这里的 "/" 表示 web 目录
  65. //request.getRequestDispatcher("/error.html").forward(request, response);
  66. response.sendRedirect(request.getContextPath() + "/error.html");
  67. }
  68. }
  69. }

2.9 功能六:跳转到修改部门的页面 DepEditServlet

思路:
获取到提交的过来的 部门编号
根据部门编号修改信息,注意:部门编号是唯一的不要被修改了
连接数据库,查询到相关信息显示到浏览器页面当中,方便用户修改

具体的代码编写如下

  1. package com.RainbowSea.servlet;
  2. import com.RainbowSea.DBUtil.DBUtil;
  3. import jakarta.servlet.ServletException;
  4. import jakarta.servlet.http.HttpServlet;
  5. import jakarta.servlet.http.HttpServletRequest;
  6. import jakarta.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.io.PrintWriter;
  9. import java.sql.Connection;
  10. import java.sql.PreparedStatement;
  11. import java.sql.ResultSet;
  12. import java.sql.SQLException;
  13. /**
  14. * 跳转到修改部门的页面
  15. */
  16. public class DepEditServlet extends HttpServlet {
  17. // 超链接是 doGet()请求
  18. // http://127.0.0.1:8080/servlet09/dept/edit?deptno=10
  19. @Override
  20. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  21. IOException {
  22. response.setContentType("text/html;charSet=UTF-8");
  23. PrintWriter writer = response.getWriter();
  24. writer.println(" <!DOCTYPE html>");
  25. writer.println("<html lang='en'>");
  26. writer.println("<head>");
  27. writer.println(" <meta charset='UTF-8'>");
  28. writer.println(" <title>部门列表页面</title>");
  29. writer.println("</head>");
  30. writer.println("<body>");
  31. writer.println(" <h1>修改部门</h1>");
  32. writer.println(" <form action='"+request.getContextPath()+"/dept/modify' method='post'>");
  33. /*
  34. 思路:
  35. 获取到提交的过来的 部门编号
  36. 根据部门编号修改信息,注意:部门编号是唯一的不要被修改了
  37. 连接数据库,查询到相关信息显示到浏览器页面当中,方便用户修改
  38. */
  39. String deptno = request.getParameter("deptno");
  40. // 连接数据库
  41. Connection connection = null;
  42. PreparedStatement preparedStatement = null;
  43. ResultSet resultSet = null;
  44. try {
  45. // 1. 注册驱动,连接数据库
  46. connection = DBUtil.getConnection();
  47. // 2. 获取到操作数据库对象,预编译SQL语句,sql测试
  48. String sql = "select dname,loc from dept where depton = ?";
  49. preparedStatement = connection.prepareStatement(sql);
  50. // 3. 填充占位符,真正执行sql语句
  51. preparedStatement.setString(1, deptno);
  52. resultSet = preparedStatement.executeQuery();
  53. // 4. 处理查询结果集
  54. while (resultSet.next()) {
  55. String dname = resultSet.getString("dname"); // 查询使用的别名,要用别名
  56. String loc = resultSet.getString("loc");
  57. // <!-- readonly 表示只读,不可修改的作用
  58. writer.println(" 部门编号: <input type='text' name='deptno' value='" + deptno + "' readonly /><br>");
  59. writer.println(" 部门名称: <input type='text' name='dname' value=" + dname + " /><br>");
  60. writer.println(" 部门位置: <input type='text' name='loc' value=" + loc + " /><br>");
  61. }
  62. } catch (SQLException e) {
  63. throw new RuntimeException(e);
  64. } finally {
  65. // 5.释放资源,最后使用的优先关闭(因为如果是关闭优先使用的话,再最后面使用的可能需要前面的资源,才能执行)
  66. DBUtil.close(connection, preparedStatement, resultSet);
  67. }
  68. writer.println(" <input type='submit' value='修改' />");
  69. writer.println(" </form>");
  70. writer.println("</body>");
  71. writer.println("</html>");
  72. }
  73. }

2.10 功能七:修改部门 DeptSaveServlet

思路:

获取到前端的提交的数据,注意 编码设置post 请求
连接数据库: 进行添加数据
添加成功: 返回部门列表页面
添加失败: 返回失败的页面

具体的代码编写如下:

  1. package com.RainbowSea.servlet;
  2. import com.RainbowSea.DBUtil.DBUtil;
  3. import jakarta.servlet.ServletException;
  4. import jakarta.servlet.http.HttpServlet;
  5. import jakarta.servlet.http.HttpServletRequest;
  6. import jakarta.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.sql.Connection;
  9. import java.sql.PreparedStatement;
  10. import java.sql.SQLException;
  11. /**
  12. * 增加部门数据
  13. */
  14. public class DeptSaveServlet extends HttpServlet {
  15. // 前端是注册信息,是post 请求
  16. @Override
  17. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  18. IOException {
  19. /*
  20. 思路:
  21. 获取到前端的提交的数据,注意 编码设置post 请求
  22. 连接数据库: 进行添加数据
  23. 添加成功: 返回部门列表页面
  24. 添加失败: 返回失败的页面
  25. */
  26. request.setCharacterEncoding("UTF-8");
  27. // 获取到前端的数据,建议 name 使用复制
  28. String deptno = request.getParameter("deptno");
  29. String dname = request.getParameter("dname");
  30. String loc = request.getParameter("loc");
  31. // 连接数据库,添加数据
  32. Connection connection = null;
  33. PreparedStatement preparedStatement = null;
  34. // 影响数据库的行数
  35. int count = 0;
  36. try {
  37. // 1. 注册驱动,连接数据库
  38. connection = DBUtil.getConnection();
  39. // 2. 获取操作数据库对象,预编译sql语句,Sql测试
  40. String sql = "insert into dept(depton,dname,loc) values(?,?,?)";
  41. preparedStatement = connection.prepareStatement(sql);
  42. // 3. 填充占位符, 真正执行sql语句,
  43. // 注意: 占位符的填充是从 1 开始的,基本上数据库相关的起始下标索引都是从 1下标开始的
  44. preparedStatement.setString(1, deptno);
  45. preparedStatement.setString(2, dname);
  46. preparedStatement.setString(3, loc);
  47. // 返回影响数据库的行数
  48. count = preparedStatement.executeUpdate();
  49. // 5.释放资源
  50. } catch (SQLException e) {
  51. throw new RuntimeException(e);
  52. } finally {
  53. DBUtil.close(connection, preparedStatement, null);
  54. }
  55. // 保存成功,返回部门列表页面
  56. if (count == 1) {
  57. // 这里应该使用,重定向
  58. // 这里用的转发,是服务器内部的,不要加项目名
  59. //request.getRequestDispatcher("/dept/list/").forward(request, response);
  60. // 重定向
  61. response.sendRedirect(request.getContextPath() + "/dept/list/");
  62. } else {
  63. // 保存失败
  64. // web当中的 html资源,这里的 "/" 表示 web 目录
  65. //request.getRequestDispatcher("/error.html").forward(request, response);
  66. response.sendRedirect(request.getContextPath() + "/error.html");
  67. }
  68. }
  69. }

3. 最后的 web.xml 配置信息

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <!-- 查看部门列表-->
  7. <servlet>
  8. <!-- 注意: 两个name值要保持一致-->
  9. <servlet-name>list</servlet-name>
  10. <servlet-class>com.RainbowSea.servlet.DeptListServlet</servlet-class>
  11. </servlet>
  12. <servlet-mapping>
  13. <servlet-name>list</servlet-name>
  14. <!-- 注意是: / 开始-->
  15. <url-pattern>/dept/list/</url-pattern>
  16. </servlet-mapping>
  17. <!-- 部门详情-->
  18. <servlet>
  19. <servlet-name>detail</servlet-name>
  20. <servlet-class>com.RainbowSea.servlet.DeptDetailServlet</servlet-class>
  21. </servlet>
  22. <servlet-mapping>
  23. <servlet-name>detail</servlet-name>
  24. <url-pattern>/dept/detail</url-pattern>
  25. </servlet-mapping>
  26. <!-- 删除部门-->
  27. <servlet>
  28. <servlet-name>delete</servlet-name>
  29. <servlet-class>com.RainbowSea.servlet.DeptDelServlet</servlet-class>
  30. </servlet>
  31. <servlet-mapping>
  32. <servlet-name>delete</servlet-name>
  33. <!-- / 开始-->
  34. <url-pattern>/dept/delete</url-pattern>
  35. </servlet-mapping>
  36. <!-- 新增部门-->
  37. <servlet>
  38. <servlet-name>save</servlet-name>
  39. <servlet-class>com.RainbowSea.servlet.DeptSaveServlet</servlet-class>
  40. </servlet>
  41. <servlet-mapping>
  42. <servlet-name>save</servlet-name>
  43. <url-pattern>/dept/save</url-pattern>
  44. </servlet-mapping>
  45. <!-- 跳转到修改部门-->
  46. <servlet>
  47. <!-- 两个 name 保持一致-->
  48. <servlet-name>edit</servlet-name>
  49. <servlet-class>com.RainbowSea.servlet.DepEditServlet</servlet-class>
  50. </servlet>
  51. <servlet-mapping>
  52. <servlet-name>edit</servlet-name>
  53. <!-- "/" 开始-->
  54. <url-pattern>/dept/edit</url-pattern>
  55. </servlet-mapping>
  56. <!-- 修改部门信息-->
  57. <servlet>
  58. <servlet-name>modify</servlet-name>
  59. <servlet-class>com.RainbowSea.servlet.DeptModifyServlet</servlet-class>
  60. </servlet>
  61. <servlet-mapping>
  62. <servlet-name>modify</servlet-name>
  63. <url-pattern>/dept/modify</url-pattern>
  64. </servlet-mapping>
  65. </web-app>

在这里插入图片描述

4. 优化方案: @WebServlet 注解 + 模板方法

由于设计到文章的篇幅过多,大家想要了解的可以移步至:?????? Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目_ChinaRainbowSea的博客-CSDN博客

5. 总结:

  1. 每次前端提交的数据都通过浏览器 F12 检查的方式,查看我们提交的数据是否,是我们需要的,是否满足条件。

  2. 每次后端从前端浏览器获取到的数据,同样都是需要打印或者调试看看,我们获取的数据是否存在错误,或者乱码的情况。

  3. 如果对SQL语句不太熟练的话,建议无论是否是简单的 SQL语句都,可以先在对应的数据库中运行测试看看,是否存在错误。

  4. 尽可能的做到,每实现一点功能就测试一下,是否存在错误,而不是一顿操作下来,虽然所以代码都编写完了,但是到最后测试的时候,一堆 BUG 。

  5. 我们应该怎么去实现一个功能呢?

    • 建议:你可以从后端往前端一步一步写。也可以从前端一步一步往后端写。都可以。但是千万要记住不要想起来什么写什么。你写代码的过程最好是程序的执行过程。也就是说:程序执行到哪里,你就写哪里。这样一个顺序流下来之后,基本上不会出现什么错误、意外。
    • 从哪里开始?
      • 假设从前端开始,那么一定是从用户点击按钮那里开始的
  6. 分析清楚哪里使用的是 doGet 请求 ,哪里使用的是 doPost 请求。

  7. 分析清楚哪里使用的是 服务器端的转发 ,哪里使用的是 重定向机制。

  8. 注意: 在服务器当需要使用到的 jar 包,必须在 WEB-INF 的目录下,创建一个名为 lib 的目录文件夹,用来存放相关的 依赖jar 包,注意路径位置不可以修改,目录文件必须为 lib 不然,当你启动的Tocmat 服务器的时候,是无法找到该对应的 依赖jar 包的。

6. 最后:

限于自身水平,其中存在的错误,希望大家,给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期!!!

在这里插入图片描述

原文链接:https://www.cnblogs.com/TheMagicalRainbowSea/p/17304969.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号