🥽 使用纯Servlet做一个单表的CRUD操作
使用纯粹的Servlet完成单表【对部门的】的增删改查操作。(B/S结构的。)
第一步:准备一张数据库表。(sql脚本)
# 部门表 drop table if exists dept; create table dept( deptno int primary key, dname varchar(255), loc varchar(255) ); insert into dept(deptno, dname, loc) values(10, 'XiaoShouBu', 'BEIJING'); insert into dept(deptno, dname, loc) values(20, 'YanFaBu', 'SHANGHAI'); insert into dept(deptno, dname, loc) values(30, 'JiShuBu', 'GUANGZHOU'); insert into dept(deptno, dname, loc) values(40, 'MeiTiBu', 'SHENZHEN'); commit; select * from dept;
第二步:准备一套HTML页面(项目原型)
- 把HTML页面准备好
- 然后将HTML页面中的链接都能够跑通。(页面流转没问题。)
- 应该设计哪些页面呢?
- 欢迎页面:index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>欢迎使用OA系统</title> </head> <body> <a href="./list.html">查看部门列表</a> </body> </html>
- 列表页面:list.html(以列表页面为核心,展开其他操作。)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>部门列表页面</title> </head> <body> <h1 align="center">部门列表</h1> <hr> <table align="center" border="1px solid black" width="50%"> <thead> <tr> <th>序号</th> <th>部门编号</th> <th>部门名称</th> <th>操作</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>10</td> <td>销售部</td> <td> <a href="">删除</a> <a href="edit.html">修改</a> <a href="detail.html">详情</a> </td> </tr> <tr> <td>1</td> <td>10</td> <td>销售部</td> <td> <a href="">删除</a> <a href="edit.html">修改</a> <a href="detail.html">详情</a> </td> </tr> <tr> <td>1</td> <td>10</td> <td>销售部</td> <td> <a href="">删除</a> <a href="edit.html">修改</a> <a href="detail.html">详情</a> </td> </tr> </tbody> </table> <hr> <a href="add.html">新增部门</a> </body> </html>
- 新增页面:add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>新增部门</title> </head> <body> <h1>新增部门</h1> <hr> <form action="./list.html" method="post"> 部门编号:<input type="text" name="dptno"><br> 部门名称:<input type="text" name="dname"><br> 部门位置:<input type="text" name="loc"><br> <input type="submit" value="新增"> </form> </body> </html>
- 修改页面:edit.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>修改部门</title> </head> <body> <h1>修改部门</h1> <hr > <form action="list.html" method="get"> <!-- 部门编号不允许修改 --> 部门编号<input type="text" name="deptno" value="20" readonly /><br> 部门名称<input type="text" name="dname" value="销售部"/><br> 部门位置<input type="text" name="loc" value="北京"/><br> <input type="submit" value="修改"/><br> </form> </body> </html>
- 详情页面:detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>部门详情</title> </head> <body> <h1>部门详情</h1> <hr> <p>部门编号:20</p> <p>部门名称:销售部</p> <p>部门位置:北京</p> <input type="button" value="后退" onclick="window.history.back()"> </body> </html>
第三步:分析我们这个系统包括哪些功能?
- 什么叫做一个功能呢?
- 只要 这个操作连接了数据库,就表示一个独立的功能。
- 包括哪些功能?
- 查看部门列表
- 新增部门
- 删除部门
- 查看部门详细信息
- 跳转到修改页面
- 修改部门
第四步:在IDEA当中搭建开发环境
- 创建一个webapp(给这个webapp添加servlet-api.jar和jsp-api.jar到classpath当中。)
- 向webapp中添加连接数据库的jar包(mysql驱动)
- 必须在WEB-INF目录下新建lib目录,然后将mysql的驱动jar包拷贝到这个lib目录下。这个目录名必须叫做lib,全部小写的。
- JDBC的工具类
package cw.javaweb.oa; import java.sql.*; import java.util.ResourceBundle; public class DBUtil { // 静态变量:在类加载时执行。 // 并且是有顺序的。自上而下的顺序。 // 属性资源文件绑定 private static ResourceBundle bundle = ResourceBundle.getBundle("resources.jdbc"); // 根据属性配置文件key获取value private static String driver = bundle.getString("driver"); private static String url = bundle.getString("url"); private static String user = bundle.getString("user"); private static String password = bundle.getString("password"); static { // 注册驱动(注册驱动只需要注册一次,放在静态代码块当中。DBUtil类加载的时候执行。) try { // "com.mysql.jdbc.Driver" 是连接数据库的驱动,不能写死。因为以后可能还会连接Oracle数据库。 // 如果连接oracle数据库的时候,还需要修改java代码,显然违背了OCP开闭原则。 // OCP开闭原则:对扩展开放,对修改关闭。(什么是符合OCP呢?在进行功能扩展的时候,不需要修改java源代码。) // Class.forName("com.mysql.cj.jdbc.Driver"); Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 私有化构造器防止工具类被实例化 */ private DBUtil() { } /** * 获取数据库连接对象 * @return * @throws SQLException */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 关闭数据库连接资源 * @param conn * @param stmt * @param rs */ public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
- 将所有HTML页面拷贝到web目录下。
第五步:实现第一个功能:查看部门列表
- 我们应该怎么去实现一个功能呢?
- 建议:你可以从后端往前端一步一步写。也可以从前端一步一步往后端写。都可以。但是千万要记住不要想起来什么写什么。你写代码的过程最好是程序的执行过程。也就是说:程序执行到哪里,你就写哪里。这样一个顺序流下来之后,基本上不会出现什么错误、意外。
- 从哪里开始?
- 假设从前端开始,那么一定是从用户点击按钮那里开始的。
- 第一:先修改前端页面的超链接,因为用户先点击的就是这个超链接。
<a href="/oa/dept/list">查看部门列表</a>
- 第二:编写web.xml文件
<servlet> <servlet-name>list</servlet-name> <servlet-class>cw.javaweb.oa.web.DeptListServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>list</servlet-name> <!--web.xml文件中的这个路径也是以“/”开始的,但是不需要加项目名--> <url-pattern>/dept/list</url-pattern> </servlet-mapping>
- 第三:编写DeptListServlet类继承HttpServlet类。然后重写doGet方法。
- 第四:在DeptListServlet类的doGet方法中连接数据库,查询所有的部门,动态的展示部门列表页面.
- 分析list.html页面中哪部分是固定死的,哪部分是需要动态展示的。
- list.html页面中的内容所有的双引号要替换成单引号,因为out.print(“”)这里有一个双引号,容易冲突。
- 现在写完这个功能之后,你会有一种感觉,感觉开发很繁琐,只使用servlet写代码太繁琐了。
package cw.javaweb.oa.web; import cw.javaweb.oa.utils.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptListServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应的内容类型以及字符集,防止中文乱码 response.setContentType("text/html;charset=UTF-8"); // 获取输出流 PrintWriter out = response.getWriter(); out.print("<!DOCTYPE html>"); out.print("<html lang='en'>"); out.print("<head>"); out.print(" <meta charset='UTF-8'>"); out.print(" <title>部门列表页面</title>"); out.print("</head>"); out.print("<body>"); out.print("<h1 align='center'>部门列表</h1>"); out.print("<hr>"); out.print("<table align='center' border='1px solid black' width='50%'>"); out.print(" <thead>"); out.print(" <tr>"); out.print(" <th>序号</th>"); out.print(" <th>部门编号</th>"); out.print(" <th>部门名称</th>"); out.print(" <th>操作</th>"); out.print(" </tr>"); out.print(" </thead>"); out.print(" <tbody>"); // 连接数据库,查询所有部门 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 获取链接 conn = DBUtil.getConnection(); String sql = "select deptno, dname, loc from dept"; // 获取预编译的数据库操作对象 ps = conn.prepareStatement(sql); // 执行sql,获取查询结果 rs = ps.executeQuery(); int i=0; while (rs.next()) { String deptno = rs.getString("deptno"); String dname = rs.getString("dname"); String loc = rs.getString("loc"); out.print(" <tr>"); out.print(" <td>"+ (i+1) +"</td>"); out.print(" <td>"+ deptno +"</td>"); out.print(" <td>"+ dname +"</td>"); out.print(" <td>"); out.print(" <a href=''>删除</a>"); out.print(" <a href=''>修改</a>"); out.print(" <a href=''>详情</a>"); out.print(" </td>"); out.print(" </tr>"); } } catch (SQLException e) { e.printStackTrace(); } finally { // 释放资源 DBUtil.close(conn, ps, rs); } out.print(" </tbody>"); out.print("</table>"); out.print("<hr>"); out.print("<a href=''>新增部门</a>"); out.print("</body>"); out.print("</html>"); } }
第六步:查看部门详情。
- 建议:从前端往后端一步一步实现。首先要考虑的是,用户点击的是什么?用户点击的东西在哪里?
- 一定要先找到用户点的“详情”在哪里。找了半天,终于在后端的java程序中找到了
<a href='写一个路径'>详情</a>
- 详情 是需要连接数据库的,所以这个超链接点击之后也是需要执行一段java代码的。所以要将这个超链接的路径修改一下。
- 注意:修改路径之后,这个路径是需要加项目名的。“/oa/dept/detail”
- 技巧:
out.print("<a href='"+contextPath+"/dept/detail?deptno="+deptno+"'>详情</a>");
- 重点:向服务器提交数据的格式:uri?name=value&name=value&name=value&name=value
- 这里的问号,必须是英文的问号。不能中文的问号。
- 解决404的问题。写web.xml文件。
<servlet> <servlet-name>detail</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptDetailServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>detail</servlet-name> <url-pattern>/dept/detail</url-pattern> </servlet-mapping>
- 编写一个类:DeptDetailServlet继承HttpServlet,重写doGet方法。
- 在doGet方法当中:连接数据库,根据部门编号查询该部门的信息。动态展示部门详情页。
package cw.javaweb.oa.web; import cw.javaweb.oa.utils.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptDetailServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取部门编号 // /oa/dept/detail?deptno=30 // 虽然是提交的30,但是服务器获取的是"30"这个字符串。 String deptno = request.getParameter("deptno"); // 设置内容类型以及编码 response.setContentType("text/html;charset=UTF-8"); // 获取输出对象 PrintWriter out = response.getWriter(); out.print("<!DOCTYPE html>"); out.print("<html lang='en'>"); out.print("<head>"); out.print(" <meta charset='UTF-8'>"); out.print(" <title>部门详情</title>"); out.print("</head>"); out.print("<body>"); out.print(" <h1>部门详情</h1>"); out.print(" <hr>"); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); String sql = "select dname, loc from dept where deptno = ?"; ps = conn.prepareStatement(sql); ps.setString(1, deptno); rs = ps.executeQuery(); if (rs.next()) { String dname = rs.getString("dname"); String loc = rs.getString("loc"); out.print(" <p>部门编号:"+ deptno +"</p>"); out.print(" <p>部门名称:"+ dname +"</p>"); out.print(" <p>部门位置:"+ loc +"</p>"); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } out.print("<input type='button' value='后退' οnclick='window.history.back()'>"); out.print("</body>"); out.print("</html>"); } }
第七步:删除部门
- 怎么开始?从哪里开始?从前端页面开始,用户点击删除按钮的时候,应该提示用户是否删除。因为删除这个动作是比较危险的。任何系统在进行删除操作之前,是必须要提示用户的,因为这个删除的动作有可能是用户误操作。(在前端页面上写JS代码,来提示用户是否删除。)
<!-- javascript:void(0) 保留超链接的样式,但是点击超链接不进行页面的跳转 --> <!-- onclick 点击超链接时执行回调函数 --> <a href="javascript:void(0)" onclick="del(30)" >删除</a> <script type="text/javascript"> function del(dno){ // confirm 弹出一个确认框,点击确定返回true,否则为false if(window.confirm("亲,删了不可恢复哦!")){ // 发送请求 // document.location.href = "" // document.location = "" // window.location.href = "" // window.location = "" document.location.href = "/oa/dept/delete?deptno=" + dno; } } </script>
- 以上的前端程序要写到后端的java代码当中:
- DeptListServlet类的doGet方法当中,使用out.print()方法,将以上的前端代码输出到浏览器上。
- 解决404的问题:
- http://localhost:8080/oa/dept/delete?deptno=30
- web.xml文件
<servlet> <servlet-name>delete</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptDelServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>delete</servlet-name> <url-pattern>/dept/delete</url-pattern> </servlet-mapping>
- 编写DeptDelServlet继承HttpServlet,重写doGet方法。
- 删除成功或者失败的时候的一个处理(这里我们选择了转发,并没有使用重定向机制。)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>error</title> </head> <body> <h1>操作失败,<a href="javascript:void(0)" onclick="window.history.back()">返回</a></h1> </body> </html>
package cw.javaweb.oa.web; import cw.javaweb.oa.utils.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptDelServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取部门id String deptno = request.getParameter("deptno"); // 连接数据库删除部门 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; int count = 0; try { conn = DBUtil.getConnection(); // 开启事务,关闭自动提交事务 conn.setAutoCommit(false); String sql = "delete from dept where id = ?"; ps = conn.prepareStatement(sql); ps.setString(1, deptno); count = ps.executeUpdate(); // 事务提交 conn.commit(); } catch (SQLException e) { // 事务回滚 try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } // 判断成功或失败,进行页面的跳转 if (count >= 1) { //删除成功 //仍然跳转到部门列表页面 //部门列表页面的显示需要执行另一个Servlet。怎么办?转发。 request.getRequestDispatcher("/dept/list").forward(request, response); } else { // 删除失败 // 请求转发可以是servlet也可以是页面 request.getRequestDispatcher("/error.html").forward(request, response); } } }
第八步:新增部门
out.print("<a href='"+contextPath+"/add.html'>新增部门</a>");
<!--新增部门 --> <servlet> <servlet-name>add</servlet-name> <servlet-class>cw.javaweb.oa.web.DeptAddServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>add</servlet-name> <url-pattern>/dept/add</url-pattern> </servlet-mapping>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>新增部门</title> </head> <body> <h1>新增部门</h1> <hr> <form action="/oa/dept/add" method="post"> 部门编号:<input type="text" name="deptno"><br> 部门名称:<input type="text" name="dname"><br> 部门位置:<input type="text" name="loc"><br> <input type="submit" value="新增"> </form> </body> </html>
package cw.javaweb.oa.web; import cw.javaweb.oa.utils.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptAddServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 注意乱码问题 request.setCharacterEncoding("UTF-8"); // 获取部门信息 String deptno = request.getParameter("deptno"); String dname = request.getParameter("dname"); String loc = request.getParameter("loc"); // 连接数据库执行插入操作 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; int count = 0; try { conn = DBUtil.getConnection(); String sql = "insert into dept(deptno, dname, loc) values (?, ?, ?)"; ps = conn.prepareStatement(sql); ps.setString(1, deptno); ps.setString(2, dname); ps.setString(3, loc); count = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } // 成功或失败 if (count >= 1) { request.getRequestDispatcher("/dept/list").forward(request, response); } else { request.getRequestDispatcher("/error.html").forward(request, response); } } }
- 注意:最后保存成功之后,转发到 /dept/list 的时候,会出现405,为什么?
- 第一:保存用的是post请求。底层要执行doPost方法。
- 第二:转发是一次请求,之前是post,之后还是post,因为它是一次请求。
- 第三:/dept/list Servlet当中只有一个doGet方法。
- 怎么解决?两种方案
- 第一种:在/dept/list Servlet中添加doPost方法,然后在doPost方法中调用doGet。
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); }
- 第二种:重定向。
第九步:跳转到修改部门的页面
out.print("<a href='"+ contextPath +"/dept/edit?deptno="+ deptno +"'>修改</a>");
<!-- 修改部门信息 --> <servlet> <servlet-name>edit</servlet-name> <servlet-class>cw.javaweb.oa.web.DeptEditServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>edit</servlet-name> <url-pattern>/dept/edit</url-pattern> </servlet-mapping>
package cw.javaweb.oa.web; import cw.javaweb.oa.utils.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptEditServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取部门编号 String deptno = request.getParameter("deptno"); // 设置响应内容格式和编码 response.setContentType("text/html;charset=UTF-8"); // 获取输出对象 PrintWriter out = response.getWriter(); out.print("<!DOCTYPE html>"); out.print("<html>"); out.print(" <head>"); out.print(" <meta charset='utf-8'>"); out.print(" <title>修改部门</title>"); out.print(" </head>"); out.print(" <body>"); out.print(" <h1>修改部门</h1>"); out.print(" <hr >"); out.print(" <form action='/oa/dept/modify' method='post'>"); out.print(" <!-- 部门编号不允许修改 -->"); // 查询数据库 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); String sql = "select dname, loc from dept where deptno = ?"; ps = conn.prepareStatement(sql); ps.setString(1, deptno); rs = ps.executeQuery(); if (rs.next()) { String dname = rs.getString("dname"); String loc = rs.getString("loc"); out.print(" 部门编号<input type='text' name='deptno' value='"+ deptno +"' readonly /><br>"); out.print(" 部门名称<input type='text' name='dname' value='"+dname+"'/><br>"); out.print(" 部门位置<input type='text' name='loc' value='"+loc+"'/><br>"); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } out.print(" <input type='submit' value='修改'/><br>"); out.print(" </form>"); out.print(" </body>"); out.print("</html>"); } }
第十步:修改部门
<!-- 编辑部门信息 --> <servlet> <servlet-name>modify</servlet-name> <servlet-class>cw.javaweb.oa.web.DeptModifyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>modify</servlet-name> <url-pattern>/dept/modify</url-pattern> </servlet-mapping>
package cw.javaweb.oa.web; import cw.javaweb.oa.utils.DBUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DeptModifyServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 注意乱码问题 request.setCharacterEncoding("UTF-8"); // 获取部门信息 String deptno = request.getParameter("deptno"); String dname = request.getParameter("dname"); String loc = request.getParameter("loc"); // 连接数据库执行插入操作 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; int count = 0; try { conn = DBUtil.getConnection(); String sql = "update dept set dname = ?, loc = ? where deptno = ?"; ps = conn.prepareStatement(sql); ps.setString(1, dname); ps.setString(2, loc); ps.setString(3, deptno); count = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } // 成功或失败 if (count >= 1) { request.getRequestDispatcher("/dept/list").forward(request, response); } else { request.getRequestDispatcher("/error.html").forward(request, response); } } }