使用“纯”Servlet做一个单表的CRUD操作
每博一文案
庄子说:"独往独来,是谓独有。独有之人,是谓至贵"。热闹是别人的狂欢,而孤独是自己的自由。
相聚总是短暂,唯孤独绵长,学会孤独为伍,才能找到浮世清欢。
金庸曾笔下人物说:"天上的白云聚了又聚,散了又散,人生离合,亦复如斯。" 人生一世,所有的人来人往,
聚散离合,都是缘分使然。缘来便聚,缘尽则散,聚是一团火,散是满天星。往后余生,与孤独和解,做自己的知音。
都说幸福的样子千篇一律,不幸的人却各有各的不幸。很多人都只能看到别人所拥有的,却没看到人所失去的。
没有人的生活是真正的容易。当你站在桥上看风景的时候,可能自己亦是别人眼中的风景。
所以不必羡慕别人,珍惜现在所拥有的,过好自己的人生才最重要。
—————— 《一禅心灵庙语》
@
- 使用“纯”Servlet做一个单表的CRUD操作
- 每博一文案
- 1. 项目说明
-
2. 具体对应的功能的代码实现
- 2.1 准备工作:
- 2.2 模块目录结构
- 2.3 工具类 DBUtil
- 2.4 功能一: index.html 该项目的欢迎页面如下:
- 2.5 功能二:部门列表 DeptListServlet
- 2.6 功能三:查看部门详情 DeptDetailServlet
- 2.7 功能四:删除部门 DeptDelServlet
- 2.8 功能五:新增部门 DeptSaveServlet
- 2.9 功能六:跳转到修改部门的页面 DepEditServlet
- 2.10 功能七:修改部门 DeptSaveServlet
- 3. 最后的 web.xml 配置信息
- 4. 优化方案: @WebServlet 注解 + 模板方法
- 5. 总结:
- 6. 最后:
1. 项目说明
介绍: 这里我们使用 纯粹 的 Servlet 完成单表【对部门的】的增删改查操作。(B/S结构的。)
结构图
初始的欢迎页面
部门列表页面
部门详情
修改部门
删除部门:
新增部门:
2. 具体对应的功能的代码实现
2.1 准备工作:
- 首先我们使用数据库,存储数据,这里我们使用的数据库是 MYSQL 。
- 我们需要准备一个名为
dept
的数据表,并插入一些数据。
create table dept (
depton int primary key,
dname varchar(255),
loc varchar(255)
);
insert into dept(depton,dname,loc) values(10,'xiaoShouBu','BEIJING');
insert into dept(depton,dname,loc) values(20,'YanFaBu','SHANGHAI');
insert into dept(depton,dname,loc) values(30,'JisShuBu','GUANGZHOU');
insert into dept(depton,dname,loc) values(40,'MeiTiBu','SHENZHEN');
select * from dept;
小技巧: MySQL 在 cmd 命令中,批量执行 sql语句的方法:如下,首先将编写好的 .sql
文件存储起来。如下图所示,
再打开cmd 进入命令窗口,再进入到Mysql当中,输入如下命令:
source 后接文件路径(要执行的批量的.sql文件)
当前数据表 dept
的信息内容如下:
- 为该模块导入 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 的配置信息
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=123
再编写好相关的:DBUtil 类 ,具体代码的编写内容如下:
package com.RainbowSea.DBUtil;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
/**
* JDBC工具类
*/
public class DBUtil {
// 静态变量,在类加载时执行
// 都是一些从 jdbc.properties 读取到的配置文件的信息
// 该方法仅仅只会读取 “.properties" 的后缀的文件,注意:默认是从src目录开始的,有子目录需要写明子目录
private static ResourceBundle bundle = ResourceBundle.getBundle("com/RainbowSea/resources/jdbc");
private static String driver = bundle.getString("driver"); // 根据properties中的name读取对应的value值
private static String url = bundle.getString("url");
private static String user = bundle.getString("user");
private static String password = bundle.getString("password");
static {
// 注册驱动(注册驱动只需要注册一次,放在静态代码当中,DBUtil类加载的时候执行)
// "com.mysql.jdbc.Driver"是连接数据库的驱动,不能写死,因为以后可能还会连接Oracle数据库。
// OCP开闭原则: 对扩展开放,对修改关闭(什么是符合 OCP呢?在进行功能扩展的时候,不需要修改java源代码)
// Class.forName("com.mysql.jdbc.Driver")
try {
Class.forName(driver); // 加载驱动
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* 获取数据库连接
* @return Connection
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}
/**
* 关闭连接
* @param connection
* @param statement
* @param resultSet
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
// 注意:分开try,最后使用的资源,优先关闭
if(resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if(statement != null) {
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if(connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
2.4 功能一: index.html 该项目的欢迎页面如下:
默认在 web 当中,一个全局配置信息当中,会将名为 index.html
的文件,设置为该项目的欢迎页面。相应的具体内容大家可以移步至 🔜🔜🔜 关于Web的欢迎页面的开发设置_ChinaRainbowSea的博客-CSDN博客
欢迎使用OA系统
查看部门列表
2.5 功能二:部门列表 DeptListServlet
注意:因为我们这里使用的是 纯 Servlet 编写的一个项目,所以在后端想要将相关的 HTML
标签相应到前端浏览器,被浏览器渲染的话,则需要特殊的方法:如下
// 设置将后端的字符串的 html 标签相应到浏览器端执行处理,并设置相应的字符集编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter();
思路:
- 在DeptListServlet类的doGet方法中连接数据库,查询所有的部门,动态的展示部门列表页面.
-
分析 html 页面中哪部分是固定死的,哪部分是需要动态展示的。
-
html页面中的内容所有的双引号要替换成单引号,因为out.print(“”)这里有一个双引号,容易冲突。
-
现在写完这个功能之后,你会有一种感觉,感觉开发很繁琐,只使用servlet写代码太繁琐了
- 我们需要连接数据库,从数据库中获取到数据,显示到前端浏览器当中。
- 注意我们这里上面的 index.html 中是通过超链接的方式,跳转到该 部门列表页面的。超链接是 doGet 请求。
package com.RainbowSea.servlet;
import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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 {
/*
说明:这里使用了doGet,和 goPost 的原因是,我们前端的 DeptSaveServlet 的新增部门,
的请求是doPost,从 doPost 请求 "转发"出来的同样是 doPost请求的,而 重定向就是doGet请求了,无论是doPost,doGet请求都是
所以这里为了,处理接受到 DeptSaveServlet 的新增部门的 "转发"请求,写了一个doPost 请求处理
*/
// 优化,将转发,替换成了 重定向的机制,(重定向的机制)是自发到浏览器前端的地址栏上的,后自发的执行
// 地址栏上是 doGet 请求的,就不需要 doPost 请求了。
/* @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
doGet(request, response); // 调用本身这里的doGet()请求
}*/
/*
因为我们前端使用的是 超链接,是goGet请求所以,
前后端的请求保持一致。
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// 设置前端浏览器显示的格式类型,以及编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 获取到该webapp的项目根路径:也就是在Tomcat 当中设置的访问的项目路径
// 注意的是: getContextPath()获取返回的路径是带有 "/项目名"的,所以不要多写了 /
String contextPath = request.getContextPath();
int i = 0;
writer.println(" ");
writer.println("");
writer.println("");
writer.println(" ");
writer.println(" 部门列表页面 ");
writer.println("");
writer.println(" ");
writer.println("");
writer.println(" 部门列表
");
writer.println(" ");
writer.println(" ");
writer.println(" 序号 ");
writer.println(" 部门编号 ");
writer.println(" 部门名称 ");
writer.println(" ");
try {
// 连接数据库,查询所有部门:
// 1. 注册驱动,获取连接
connection = DBUtil.getConnection();
// 2. 获取操作数据库对象,预编译sql语句
String sql = "select depton as det,dname,loc from dept"; // 在mysql中测试一下是否正确
preparedStatement = connection.prepareStatement(sql);
// 3. 执行sql语句
resultSet = preparedStatement.executeQuery();
// 4. 处理查询结果集
while (resultSet.next()) {
String det = resultSet.getString("det"); // 有别名要使用别名
String dname = resultSet.getString("dname");
String loc = resultSet.getString("loc");
writer.print(" ");
writer.print(" " + (++i) + " ");
writer.print(" " + det + " ");
writer.print(" " + dname + " ");
writer.print(" ");
writer.print(" 删除");
// 将部门编号传过去,用户数据库查询修改
writer.print(" 修改");
//注意这里的是前端的资源,需要加项目名,但是这里的项目名我们通过 getContestPath()方法动态获取
// 并且将部门名传过去,再从数据库当中查找出来对应的部门的详细信息:注意: ?(间隔) Http传输协议
writer.print(" 详情");
writer.print(" ");
writer.print(" ");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5. 关闭资源
DBUtil.close(connection, preparedStatement, resultSet);
}
writer.println("
");
writer.println("
");
// 前端的资源路径访问需要加项目名
writer.println("新增部门");
writer.println("");
writer.println("");
}
}
2.6 功能三:查看部门详情 DeptDetailServlet
建议:从前端往后端一步一步实现。首先要考虑的是,用户点击的是什么?用户点击的东西在哪里?
一定要先找到用户点的“详情”在哪里。这里我们使用超链接的形式处理
详情
详情 是需要连接数据库的,所以这个超链接点击之后也是需要执行一段java代码的。所以要将这个超链接的路径修改一下。
注意:修改路径之后,这个路径是需要加项目名的。”/servlet09/dept/detail”
技巧:
out.print("详情");
重点:向服务器提交数据的格式:uri?name=value&name=value&name=value&name=value
。这里的问号,必须是英文的问号。不能中文的问号。
思路:
中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
第一步:获取部门编号
第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
第三步:将部门信息响应到浏览器上。(显示一个详情。)
package com.RainbowSea.servlet;
import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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 {
// 前端超连接是 doGet()请求
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// 设置前端浏览器格式类型和字符集编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
//中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
// 第一步:获取部门编号
// 第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
// 第三步:将部门信息响应到浏览器上。(显示一个详情。)
// 1.
// http://127.0.0.1:8080/servlet09/dept/detail?deptno=40
String deptno = request.getParameter("deptno"); // 注意是我们前端提交的数据,建议复制name
try {
// 2. 连接数据库,根据部门编号查询数据库
// 1.注册驱动,连接数据库
connection = DBUtil.getConnection();
// 2. 预编译SQL语句,sql要测试
String sql = "select dname,loc from dept where depton = ?"; // ? 占位符
preparedStatement = connection.prepareStatement(sql);
// 3. 填充占位符,真正执行sql语句
preparedStatement.setString(1, deptno);
resultSet = preparedStatement.executeQuery();
// 4. 处理查询结果集
while (resultSet.next()) {
String dname = resultSet.getString("dname");
String loc = resultSet.getString("loc");
// 注意将 “双引号转换为单引号,因为在Java当中不可以嵌套多个双引号,除非是字符串的拼接
// 所以使用 '单引号
writer.println(" ");
writer.println(" 部门详情
");
writer.println(" 部门编号: " + deptno + "
");
writer.println(" 部门名称: " + dname + "
");
writer.println(" 部门位置: " + loc + "
");
writer.println(" ");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5. 释放资源
DBUtil.close(connection, preparedStatement, resultSet);
}
writer.println("");
writer.println("
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net