【JavaEE】Servlet:表白墙
文章目录
- 一、前端
- 二、前置知识
- 三、代码
- 1、后端
- 2、前端
- 3、总结
- 四、存入数据库
- 1、引入 mysql 的依赖,mysql 驱动包
- 2、创建数据库数据表
- 3、调整上述后端代码
- 3.1 封装数据库操作,和数据库建立连接
- 3.2 调整后端代码
一、前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表白墙</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 800px;
margin: 0 auto;
}
.container h2 {
text-align: center;
margin: 30px;
}
.row {
height: 50px;
display: flex;
justify-content: center;
margin-top: 5px;
line-height: 50px;
}
.row span {
height: 50px;
width: 100px;
line-height: 50px;
}
.row input {
height: 50px;
width: 300px;
line-height: 50px;
}
.row button {
height: 50px;
width: 400px;
margin: 10px 0px;
color: white;
background-color: orange;
border: none;
border-radius: 10px;
}
.row button:active {
background-color: grey;
}
</style>
</head>
<body>
<div class="container">
<h2>表白墙</h2>
<div class="row">
<span>谁</span>
<input type="text" id="from">
</div>
<div class="row">
<span>对谁</span>
<input type="text" id="to">
</div>
<div class="row">
<span>说</span>
<input type="text" id="message">
</div>
<div class="row">
<button>提交</button>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
let container = document.querySelector('.container');
let fromInput = document.querySelector('#from');
let toInput = document.querySelector('#to');
let messageInput = document.querySelector('#message');
let button = document.querySelector('button');
button.onclick = function() {
let from = fromInput.value;
let to = toInput.value;
let message = messageInput.value;
if(from=='' || to=='' || message=='||') {
return;
}
let newDiv = document.createElement('div');
newDiv.className = 'row';
newDiv.innerHTML = from + "对" + to + "说" + message;
container.appendChild(newDiv);
fromInput.value = '';
toInput.value = '';
messageInput.value = '';
}
</script>
</body>
</html>
二、前置知识
pom.xml 里面的依赖确保了在开发阶段项目能够编译和运行,但在部署到Tomcat服务器时,Tomcat会提供这些类,因此不需要将它们打包到最终的WAR文件中。
运行项目一般是两级路径:
- 第一级:Context path:
context path代表了当前的 webapp(网站),一个 tomcat 上是可以同时部署多个 webapp(网站)的,webapps 目录下的每个目录都是一个单独的 webapp,所以有的资料也把tomcat叫做容器。
如何确定 Context path:
(1)如果是通过 startup.bat 启动的 tomcat,webapps 里对应的 war 包名/目录名就是这个 webapp 的 Context path;
(2)如果是通过 smart tomcat 启动 tomcat,是在启动的配置项中手动指定的 Context path(这种是另外的一种运行 tomcat 的方式) - 第二级路径:就是 ServletPath
这个是根据代码中的@WebServlet
注解来确定的或者就是 webapp下面的静态文件/目录
以下是完整项目的目录:
三、代码
1、后端
创建 Message.java 和 MessageServlet.java
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// 对应到前端传来的请求 body 格式
// 此处要保证每个属性名字和 JSON 里的 key 对应
// 同时要保证这几个格式是 public 或者提供 public 的 getter 方法
class Message {
public String from;
public String to;
public String message;
@Override
public String toString() {
return "Message{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", message='" + message + '\'' +
'}';
}
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
List<Message> messageList = new ArrayList<>();
// 负责实现让服务器从客户端拿数据
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 把 body 的 json 数据解析出来, json格式的字符串(通过输入流获取) -> 对象
Message message = objectMapper.readValue(req.getInputStream(), Message.class);
// 2. 把这个数据保存起来,最简单的是保存到内存中
messageList.add(message);
System.out.println("message: " + message);
// 3. 返回成功的响应
resp.setContentType("application/json;charset=utf8"); // application/json;charset=utf8
resp.getWriter().write("{\"ok\": 1}");
}
// 负责实现让客户端从服务器拿数据
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
// 对象 -> json格式的字符串
String respString = objectMapper.writeValueAsString(messageList);
resp.getWriter().write(respString);
}
}
2、前端
事实上只有注释行为新的步骤(也就是第103行)后面才是更新的代码,前面和上面的前端代码一样。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表白墙</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 800px;
margin: 0 auto;
}
.container h2 {
text-align: center;
margin: 30px;
}
.row {
height: 50px;
display: flex;
justify-content: center;
margin-top: 5px;
line-height: 50px;
}
.row span {
height: 50px;
width: 100px;
line-height: 50px;
}
.row input {
height: 50px;
width: 300px;
line-height: 50px;
}
.row button {
height: 50px;
width: 400px;
margin: 10px 0px;
color: white;
background-color: orange;
border: none;
border-radius: 10px;
}
.row button:active {
background-color: grey;
}
</style>
</head>
<body>
<div class="container">
<h2>表白墙</h2>
<div class="row">
<span>谁</span>
<input type="text" id="from">
</div>
<div class="row">
<span>对谁</span>
<input type="text" id="to">
</div>
<div class="row">
<span>说</span>
<input type="text" id="message">
</div>
<div class="row">
<button>提交</button>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
let container = document.querySelector('.container');
let fromInput = document.querySelector('#from');
let toInput = document.querySelector('#to');
let messageInput = document.querySelector('#message');
let button = document.querySelector('button');
button.onclick = function() {
let from = fromInput.value;
let to = toInput.value;
let message = messageInput.value;
if(from=='' || to=='' || message=='||') {
return;
}
let newDiv = document.createElement('div');
newDiv.className = 'row';
newDiv.innerHTML = from + "对" + to + "说" + message;
container.appendChild(newDiv);
fromInput.value = '';
toInput.value = '';
messageInput.value = '';
// 新的步骤,将刚才输入框里取得的数据构造成 POST 请求,提交给后端服务器
// json
let messageJson = {
// 字符串: 变量
from: from,
to: to,
message: message
};
$.ajax({
type: 'post',
// 相对路径
url: 'message',
// 绝对路径
// url: '/MessageWall1/message',
contentType: 'application/json;charset=utf8',
data: JSON.stringify(messageJson), // JSON 转成 JSON 格式的字符串
success: function() {
alert("提交成功");
},
// 返回状态码不是2xx就触发此函数
error: function() {
alert("提交失败");
}
});
}
// 这个函数在页面加载/刷新的时候调用,通过这个函数从服务器获取到当前的消息列表,显示在页面上
function load() {
$.ajax({
type: 'get',
url: 'message',
success: function(body) {
// 此处 body 已经是 json , ajax 会根据contentType自动转换
let container = document.querySelector('.container');
for(let message of body) {
let newDiv = document.createElement('div');
newDiv.className = 'row';
newDiv.innerHTML = message.from + " 对 " + message.to + " 说 " + message.message;
container.appendChild(newDiv);
}
}
});
}
// 函数调用写在这里就表示页面加载的时候来执行
load();
</script>
</body>
</html>
json的key只能是字符串类型.此处写的 from其实是"from" .只不过咱们这里图省事,把"省略了
json中表示字符串,单弓|号或者双引号都行.
3、总结
- 打开页面/刷新页面,先执行前端load();
- load()会执行ajax,ajax会发生一个HTTP请求给服务器,GET /message,这里面的success先不执行,后面才会执行
- 这个HTTP请求通过网络发送给tomcat,进一步触发doGet方法,doGet方法执行里面的逻辑,将List转换成JSON,构造HTTP响应返回给客户端
- 客户端收到返回数据触发回调函数,也就是success,success执行里面的逻辑,将服务器返回的数据(body)显示到页面上
四、存入数据库
当服务器重启,List 里面的数据会丢失,应该怎样解决这个问题?
关键是要把数据存储在服务器的硬盘上面
1、存入文件里面,使用 IO 流来写文件/读文件
2、存入数据库里面,使用 MYSQL+JDBC
JDBC(Java Database Connectivity)是Java编程语言中用于执行SQL语句的一组API(应用程序接口)。它为数据库访问提供了一种标准的方法,使得Java程序可以与各种关系型数据库进行交互,而无需关心具体的数据库实现细节。
这里使用存入数据库的方式来解决问题:
1、引入 mysql 的依赖,mysql 驱动包
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
2、创建数据库数据表
create database MessageWall;
use MessageWall;
drop table if exists MessageWall;
create table MessageWall (
`from` varchar(100),
`to` varchar(100),
message varchar(1024)
);
3、调整上述后端代码
3.1 封装数据库操作,和数据库建立连接
新建一个类 DBUtil.java
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
//import com.mysql.jdbc.Connection;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
// 通过这个类完成建立数据库的连接的过程
// 建立连接需要使用 DataSource,并且一个程序有一个 DataSource 实例即可,这里用单例模式来实现
public class DBUtil {
private static DataSource dataSource = null;
private static DataSource getDataSource() {
if (dataSource == null) {
dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1/messagewall?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("1234");
}
return dataSource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
// 后创建的先释放
// 此处分开写 try-catch 是因为一个地方异常了不会影响其他的 close 执行
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.2 调整后端代码
MessageServlet.java
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
// List<Message> messageList = new ArrayList<>();
// 负责实现让服务器从客户端拿数据
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 把 body 的 json 数据解析出来, json格式的字符串(通过输入流获取) -> 对象
Message message = objectMapper.readValue(req.getInputStream(), Message.class);
// 2. 把这个数据保存起来,最简单的是保存到内存中
// messageList.add(message);
save(message);
System.out.println("message: " + message);
// 3. 返回成功的响应
resp.setContentType("application/json;charset=utf8"); // application/json;charset=utf8
resp.getWriter().write("{\"ok\": 1}");
}
// 负责实现让客户端从服务器拿数据
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
// 对象 -> json格式的字符串
List<Message> messageList= load();
String respString = objectMapper.writeValueAsString(messageList);
resp.getWriter().write(respString);
}
// 把当前的消息存到数据库中
private void save(Message message) {
Connection connection = null;
PreparedStatement statement = null;
try {
// 1.和数据库建立连接
connection = DBUtil.getConnection();
// 2.构造SQL语句
String sql = "insert into message values(?, ?, ?)";
statement = connection.prepareStatement(sql);
statement.setString(1, message.from);
statement.setString(2, message.to);
statement.setString(3, message.message);
// 3.执行SQL语句
int ret = statement.executeUpdate();
if (ret != 1) {
System.out.println("插入失败");
} else {
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 4.关闭连接
DBUtil.close(connection, statement, null);
}
}
// 从数据库查询到记录
private List<Message> load() {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
List<Message> messageList = new ArrayList<>();
try {
// 1.建立连接
connection = DBUtil.getConnection();
// 2.构造SQL语句
String sql = "select * from message";
statement = connection.prepareStatement(sql);
// 3.执行SQL
resultSet = statement.executeQuery(sql);
// 4.遍历结果集
while (resultSet.next()) {
Message message = new Message();
message.from = resultSet.getString("from");
message.to = resultSet.getString("to");
message.message = resultSet.getString("message");
messageList.add(message);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5.关闭连接
DBUtil.close(connection, statement, resultSet);
}
return messageList;
}
}