表白墙 -- 前后端代码详解
一、前端
先提供一个之前写过的表白墙的前端代码:
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>表白墙</title></head><body><style>.container{width: 400px;/* margin 外边距. 第一个数字上下外边距, 第二个数字表示水平外边距. 如果水平设置成 auto 表示元素就水平居中~~ */margin: 0 auto;}h1{text-align: center;}p{text-align: center;color: #666;}.row{height: 40px;display: flex;/* 水平居中 */justify-content: center;/* 垂直居中 */align-items: center;}.row span{width: 100px;}.row input{width: 200px;height: 30px;}.row button{width: 306px;height: 40px;color: white;background: orange;border: none;}.row button:active{background-color: #666;}</style><divclass="container"><h1>表白墙</h1><p>输入后点击提交, 就会把信息显示在表格中</p><divclass="row"><span>谁: </span><inputtype="text"></div><divclass="row"><span>对谁: </span><inputtype="text"></div><divclass="row"><span>说: </span><inputtype="text"></div><divclass="row"><button>提交</button></div></div><script>let container = document.querySelector('.container');let button = document.querySelector('button');
button.onclick=function(){// 1. 获取到输入框的内容let inputs = document.querySelectorAll('input');let from = inputs[0].value;let to = inputs[1].value;let message = inputs[2].value;if(from ==''|| to ==''|| message ==''){alert('当前输入框内容为空!');return;}
console.log(from +", "+ to +", "+ message);// 2. 能够构造出新的 div, 用来保存用户提交的内容let rowDiv = document.createElement('div');
rowDiv.className ='row';
rowDiv.innerHTML = from +" 对 "+ to +" 说: "+ message;
container.appendChild(rowDiv);// 3. 提交完之后, 清空输入框的内容for(let i =0; i < inputs.length; i++){
inputs[i].value ='';}}</script></body></html>
形如:
二、后端实现
2.1 需求
当我们输入一些内容:
但是一旦刷新后,所有数据都没了!
因为当前的数据只是在内存中存储的!关了再开,内存中原来的数据就没了 ~~
这样显然不能达到我们的预期。解决方案:
需要让用户留言的数据能够在服务器这边保存,就可以保证即使页面关闭,数据也不会丢失!!!
- 点击提交,希望数据能够在服务器这里保存,势必要把数据发给服务器 **(存档)**;
- 当关闭页面再打开的时候,就需要从服务器获取到之前保存过的数据,在页面上显示出来 **(读档)**!
2.2 创建项目及初始化
参考博客:https://blog.csdn.net/yyhgo_/article/details/128468738?spm=1001.2014.3001.5501
创建一个新的 Maven 项目!(引入 Servlet 依赖 和 Jackson 依赖)
pom.xml:
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>MessageWall</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.4</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency></dependencies></project>
把之前实现的表白墙前端页面文件拷贝到 webapp 目录中!
2.3 实现提交数据 (存档)
2.3.1 实现 doPost
首先创建一个 Message 类:
publicclassMessage{privateString from;privateStringto;privateString message;publicStringgetFrom(){return from;}publicvoidsetFrom(String from){this.from = from;}publicStringgetTo(){returnto;}publicvoidsetTo(Stringto){this.to=to;}publicStringgetMessage(){return message;}publicvoidsetMessage(String message){this.message = message;}}
MessageServlet 类:
importcom.fasterxml.jackson.databind.ObjectMapper;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.util.ArrayList;importjava.util.List;@WebServlet("/message")publicclassMessageServletextendsHttpServlet{// 这个对象在多个方法中都需要使用privateObjectMapper objectMapper =newObjectMapper();privateList<Message> messageList =newArrayList<>();// 负责让页面获取到数据@OverrideprotectedvoiddoGet(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{}// 提交数据@OverrideprotectedvoiddoPost(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{// 获取到 body 中的数据并解析Message message = objectMapper.readValue(req.getInputStream(),Message.class);// 把这个 message 保存一下. 简单的办法就是保存在内存中.
messageList.add(message);
resp.setStatus(200);System.out.println("提交数据成功: from="+ message.getFrom()+", to="+ message.getTo()+", message="+ message.getMessage());}}
2.3.2 构造请求 (修改 html 文件)
补充 第4步:
点击发送按钮,给服务器发个 post 请求 ~~
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>表白墙</title></head><body><style>.container{width: 400px;/* margin 外边距. 第一个数字上下外边距, 第二个数字表示水平外边距. 如果水平设置成 auto 表示元素就水平居中~~ */margin: 0 auto;}h1{text-align: center;}p{text-align: center;color: #666;}.row{height: 40px;display: flex;/* 水平居中 */justify-content: center;/* 垂直居中 */align-items: center;}.row span{width: 100px;}.row input{width: 200px;height: 30px;}.row button{width: 306px;height: 40px;color: white;background: orange;border: none;}.row button:active{background-color: #666;}</style><divclass="container"><h1>表白墙</h1><p>输入后点击提交, 就会把信息显示在表格中</p><divclass="row"><span>谁: </span><inputtype="text"></div><divclass="row"><span>对谁: </span><inputtype="text"></div><divclass="row"><span>说: </span><inputtype="text"></div><divclass="row"><button>提交</button></div><!-- 需要构造出一个形如 --><!-- <div class="row">
黑猫 对 白猫 说: 喵
</div> --></div><scriptsrc="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script>let container = document.querySelector('.container');let button = document.querySelector('button');
button.onclick=function(){// 1. 获取到输入框的内容let inputs = document.querySelectorAll('input');let from = inputs[0].value;let to = inputs[1].value;let message = inputs[2].value;if(from ==''|| to ==''|| message ==''){alert('当前输入框内容为空!');return;}
console.log(from +", "+ to +", "+ message);// 2. 能够构造出新的 div, 用来保存用户提交的内容let rowDiv = document.createElement('div');
rowDiv.className ='row';
rowDiv.innerHTML = from +" 对 "+ to +" 说: "+ message;
container.appendChild(rowDiv);// 3. 提交完之后, 清空输入框的内容for(let i =0; i < inputs.length; i++){
inputs[i].value ='';}// 4. 点击发送按钮, 给服务器发个 post 请求. let data ={from: from,to: to,message: message
};
$.ajax({type:'post',url:'message',// url: '/java105/message',// 这里放的是 body 的内容data:JSON.stringify(data),contentType:"application/json; charset=utf8",success:function(body){
console.log("提交数据成功");}});}</script></body></html>
细节:
2.3.3 验证
1)启动 tomcat 服务器
2)通过 表白墙.html 发送请求
访问 http://127.0.0.1:8080/MessageWall/表白墙.html:
过程中使用 Fiddler 抓包:
请求:
服务器解析结果:
响应:
2.4 实现获取数据 (读档)
2.4.1 实现 doGet
MessageServlet 类:
importcom.fasterxml.jackson.databind.ObjectMapper;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.util.ArrayList;importjava.util.List;@WebServlet("/message")publicclassMessageServletextendsHttpServlet{// 这个对象在多个方法中都需要使用privateObjectMapper objectMapper =newObjectMapper();privateList<Message> messageList =newArrayList<>();// 负责让页面获取到数据@OverrideprotectedvoiddoGet(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{// body 就是响应的 body 内容 (json 数组).// jquery 非常智能的帮我们把 json 数组给解析成了 js 对象数组// 但是有个前提条件, 就是响应的 Content-Type 得是 application/json
resp.setContentType("application/json; charset=utf8");// 把 messageList 转成 json 字符串,并且返回给页面就行了.
resp.getWriter().write(objectMapper.writeValueAsString(messageList));}// 提交数据@OverrideprotectedvoiddoPost(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{// 获取到 body 中的数据并解析Message message = objectMapper.readValue(req.getInputStream(),Message.class);// 把这个 message 保存一下. 简单的办法就是保存在内存中.
messageList.add(message);
resp.setStatus(200);System.out.println("提交数据成功: from="+ message.getFrom()+", to="+ message.getTo()+", message="+ message.getMessage());}}
2.4.2 构造请求 (修改 html 文件)
什么时候来发这个请求呢?
页面加载的时候,我们就希望进行读档操作!
==================================================================================
在 html 文件中,哪里是页面加载的时候呢?
直接写在 script 标签里的代码,都是在页面加载的时候执行的!
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>表白墙</title></head><body><style>.container{width: 400px;/* margin 外边距. 第一个数字上下外边距, 第二个数字表示水平外边距. 如果水平设置成 auto 表示元素就水平居中~~ */margin: 0 auto;}h1{text-align: center;}p{text-align: center;color: #666;}.row{height: 40px;display: flex;/* 水平居中 */justify-content: center;/* 垂直居中 */align-items: center;}.row span{width: 100px;}.row input{width: 200px;height: 30px;}.row button{width: 306px;height: 40px;color: white;background: orange;border: none;}.row button:active{background-color: #666;}</style><divclass="container"><h1>表白墙</h1><p>输入后点击提交, 就会把信息显示在表格中</p><divclass="row"><span>谁: </span><inputtype="text"></div><divclass="row"><span>对谁: </span><inputtype="text"></div><divclass="row"><span>说: </span><inputtype="text"></div><divclass="row"><button>提交</button></div><!-- 需要构造出一个形如 --><!-- <div class="row">
黑猫 对 白猫 说: 喵
</div> --></div><scriptsrc="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script>let container = document.querySelector('.container');let button = document.querySelector('button');
button.onclick=function(){// 1. 获取到输入框的内容let inputs = document.querySelectorAll('input');let from = inputs[0].value;let to = inputs[1].value;let message = inputs[2].value;if(from ==''|| to ==''|| message ==''){alert('当前输入框内容为空!');return;}
console.log(from +", "+ to +", "+ message);// 2. 能够构造出新的 div, 用来保存用户提交的内容let rowDiv = document.createElement('div');
rowDiv.className ='row';
rowDiv.innerHTML = from +" 对 "+ to +" 说: "+ message;
container.appendChild(rowDiv);// 3. 提交完之后, 清空输入框的内容for(let i =0; i < inputs.length; i++){
inputs[i].value ='';}// 4. 点击发送按钮, 给服务器发个 post 请求. let data ={from: from,to: to,message: message
};
$.ajax({type:'post',url:'message',// url: '/java105/message',// 这里放的是 body 的内容data:JSON.stringify(data),contentType:"application/json; charset=utf8",success:function(body){
console.log("提交数据成功");}});}// 直接写在 script 标签里的代码, 都是在页面加载的时候执行的. // 来获取服务器的数据functiongetMessages(){
$.ajax({type:'get',url:'message',success:function(body){// body 就是响应的 body 内容 (json 数组). // jquery 非常智能的帮我们把 json 数组给解析成了 js 对象数组// 但是有个前提条件, 就是响应的 Content-Type 得是 application/jsonlet container = document.querySelector('.container');// 观察 html, .row 都是在 .container 中存放的,所以获取到.container,把新的 .row 都加进去! for(let i =0; i < body.length; i++){let message = body[i];// 根据这个元素构造一个 div.row, 来容纳数据// <div class="row">// 黑猫 对 白猫 说: 喵// </div>let row = document.createElement('div');
row.className ='row';
row.innerHTML = message.from +" 对 "+ message.to +" 说: "+ message.message;
container.appendChild(row);}}});}getMessages();</script></body></html>
function 中的参数 body 就是响应的 body 内容 (json 数组)!
jquery 非常智能地帮我们把 json 数组给解析成了 js 对象数组!但是有个前提条件,就是响应的 Content-Type 得是 application/json 格式!
如果 jquery 没做这个事情,就需要使用 JSON.parse 来完成 ~~
2.4.3 验证
1)重新启动 tomcat 服务器
2)访问 http://127.0.0.1:8080/MessageWall/表白墙.html
此时提交数据后,无论怎样刷新,数据仍然保留!!!成功 ~
服务器重启,原有的数据就没了 ~~
页面刷新过程中使用 Fiddler 抓包:
请求:
响应:
三、JDBC 版本 (MySQL)
我们这个程序目前最大的问题就是:数据在内存中,服务器一旦重启就没了。我们需要让数据在数据库中持久化保存!JDBC!!!
3.1 引入 JDBC 驱动包
中央仓库中搜索 mysql,选择:
这里选择 5.1.49:
将代码拷贝到 pom.xml 中:
3.2 设计数据库表结构
设计有几张表,表里有什么 ~~
1.message 表;
2.三个列:from、to、message
注意: from 等关键字,需要加上反引号
``
!!!
3.3 修改代码
Java的JDBC编程 博客链接:https://blog.csdn.net/yyhgo_/article/details/128061324?spm=1001.2014.3001.5501
通过数据库来 存 / 读 数据,就不再需要 ArrayList 了 ~~
主要是两个方法:
private void save(Message message)
:把数据保存到数据库中private List<Message> load()
:从数据库查询数据
完整代码:
publicclassMessage{privateString from;privateStringto;privateString message;publicStringgetFrom(){return from;}publicvoidsetFrom(String from){this.from = from;}publicStringgetTo(){returnto;}publicvoidsetTo(Stringto){this.to=to;}publicStringgetMessage(){return message;}publicvoidsetMessage(String message){this.message = message;}}
importcom.mysql.jdbc.jdbc2.optional.MysqlDataSource;importjavax.sql.DataSource;// 使用这个类来封装 DataSource 的单例publicclassDBUtil{privatestaticvolatileDataSource dataSource =null;privateDBUtil(){}publicstaticDataSourcegetDataSource(){if(dataSource ==null){synchronized(DBUtil.class){if(dataSource ==null){
dataSource =newMysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java105?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");// 数据库密码.((MysqlDataSource)dataSource).setPassword("632yyh..");}}}return dataSource;}}
importcom.fasterxml.jackson.databind.ObjectMapper;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.sql.DataSource;importjava.io.IOException;importjava.sql.Connection;importjava.sql.PreparedStatement;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.util.ArrayList;importjava.util.List;@WebServlet("/message")publicclassMessageServletextendsHttpServlet{// 这个对象在多个方法中都需要使用privateObjectMapper objectMapper =newObjectMapper();// 负责让页面获取到数据@OverrideprotectedvoiddoGet(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{// 显式声明当前的响应数据格式 不要让客户端去猜!!!
resp.setContentType("application/json; charset=utf8");// 把 messageList 转成 json 字符串, 并且返回给页面就行了.List<Message> messageList =null;try{
messageList =load();}catch(SQLException e){
e.printStackTrace();}
resp.getWriter().write(objectMapper.writeValueAsString(messageList));}// 提交数据@OverrideprotectedvoiddoPost(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{// 获取到 body 中的数据并解析Message message = objectMapper.readValue(req.getInputStream(),Message.class);// 把这个 message 保存一下. 简单的办法就是保存在内存中.// messageList.add(message);try{save(message);}catch(SQLException e){
e.printStackTrace();}
resp.setStatus(200);System.out.println("提交数据成功: from="+ message.getFrom()+", to="+ message.getTo()+", message="+ message.getMessage());}privatevoidsave(Message message)throwsSQLException{// 把数据保存到数据库中// 1. 先有一个数据源DataSource dataSource =DBUtil.getDataSource();// 2. 建立连接Connection connection = dataSource.getConnection();// 3. 构造 SQLString sql ="insert into message values(?, ?, ?)";PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, message.getFrom());
statement.setString(2, message.getTo());
statement.setString(3, message.getMessage());// 4. 执行 SQLint ret = statement.executeUpdate();System.out.println("ret = "+ ret);// 5. 关闭连接
statement.close();
connection.close();}privateList<Message>load()throwsSQLException{// 从数据库查询数据// 1. 先有一个数据源DataSource dataSource =DBUtil.getDataSource();// 2. 建立连接Connection connection = dataSource.getConnection();// 3. 构造 SQLString sql ="select * from message";PreparedStatement statement = connection.prepareStatement(sql);// 4. 执行 SQLResultSet resultSet = statement.executeQuery();// 5. 遍历结果集合List<Message> messageList =newArrayList<>();while(resultSet.next()){Message message =newMessage();
message.setFrom(resultSet.getString("from"));
message.setTo(resultSet.getString("to"));
message.setMessage(resultSet.getString("message"));
messageList.add(message);}// 6. 关闭连接
statement.close();
connection.close();return messageList;}}
细节:
数据源不需要创建多份,因此可以使用单例模式:单独构造一个 类DBUtil !
Servlet 代码中是否涉及到 多线程/线程安全 的问题呢?
Servlet 写的是一个服务器。同一时刻,可能要处理多个客户端的请求!
一旦同时有多个客户端都发来请求,服务器势必就需要同时处理多个请求。Tomcat 内部正是使用了多线程的方式来处理的 ~~
3.4 验证
1)重新启动 tomcat 服务器
2)访问 http://127.0.0.1:8080/MessageWall/表白墙.html
此时提交数据后,无论怎样刷新,还是重新启动服务器,数据仍然保留!!!成功 ~
此时查看数据库中 message 表的数据:
完!提前祝大家 2023 新年快乐!~~
版权归原作者 yyhgo_ 所有, 如有侵权,请联系我们删除。