用servlet写的博客系统

article/2025/6/24 2:47:29

数据库的设计:

设计好对应的表结构,把数据库相关的代码封装起来。

a)找到实体:

博客(blog表)

用户(user表)

b)确认实体之间的关系

一对多

一个博客属于一个用户,一个用户可以发多个博客。

所以在blog表里,应该有userId这一属性列

用户表:

博客表:

指令如下:

create database if not exists blog_system charset utf8;use blog_system;drop table if exists blog;
drop table if exists user;create table blog (//主键blogId int primary key auto_increment,title varchar(1024),content varchar(4096),postTime datetime,userId int
);create table user (//主键userId int primary key auto_increment,//unique 唯一username varchar(50) unique,password varchar(50)
)insert into blog values(1,'第一篇博客','我要好好写代码',now(),1);
insert into blog values (2,'第二篇博客','我要好好写代码',now(),1);
insert into blog values (3,'第三篇博客','我要好好写代码',now(),1);insert into user values (1,'zhangsan','123');
insert into user values (2,'lisi','234');

采用MVC结构:

M(model):操作数据的代码

V(view):操作/构造页面的代码

C(controller):业务逻辑。处理前端的请求

model

DBUtil:

package model;import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class DBUtil {//通过这个类,封装数据库建立连接和释放资源的操作//因为多个servlet都需要使用到数据库,所以需要一个单独的地方来封装datasourceprivate static volatile DataSource dataSource = null;//创建数据源//此处使用单例模式,当tomcat收到多个请求时,不会出现线程不安全的情况。private static DataSource getDataSource() {if(dataSource == null) {synchronized(DBUtil.class) {if(dataSource == null) {dataSource = new MysqlDataSource();((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/blogSystem?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) {//这里采用一个个回收,是防止当一个出现问题时,其它正常的依然能回收,不会堵住。if(resultSet != null) {try {resultSet.close();} catch (SQLException e) {throw new RuntimeException(e);}}if(preparedStatement != null) {try {preparedStatement.close();} catch (SQLException e) {throw new RuntimeException(e);}}if(connection != null) {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}
}

Blog:

package model;import java.sql.Timestamp;//创建实体类
//每一个表都需要一个专门的类来表示
//数据库里的每一条数据,都对应到这个类的一个对象
//Blog对象对应到blog表里的一条记录
//表里有哪些列,这个类里就应该有哪些属性
public class Blog {private int blogId;private String title;private String content;private Timestamp postTime;private int userId;public int getBlogId(){return blogId;}public void setBlogId(int blogId){this.blogId = blogId;}public String getTitle(){return title;}public void setTitle(String title){this.title = title;}public String getContent(){return content;}public void setContent(String content){this.content = content;}public Timestamp getPostTime(){return postTime;}public void setPostTime(Timestamp postTime){this.postTime = postTime;}public int getUserId(){return userId;}public void setUserId(int userId){this.userId = userId;}@Overridepublic String toString() {return "Blog{" +"blogId=" + blogId +", title='" + title + '\'' +", content='" + content + '\'' +", postTime=" + postTime +", userId=" + userId +'}';}}

User:

package model;public class User {private int userId;private String username;private String password;public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User{" +"userId=" + userId +", username=" + username +", password='" + password + '\'' +'}';}}

BlogDAO:

package model;//这个类主要是针对博客表的增删改查
//DAO:Data Access Project 数据访问对象,通过类的对象,完成对数据库表的操作import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;public class BlogDAO {//新增public void insert(Blog blog) {Connection connection = null;PreparedStatement preparedStatement = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "insert into blog values(null,?,?,now(),?)";preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, blog.getTitle());preparedStatement.setString(2,blog.getContent());preparedStatement.setInt(3,blog.getUserId());//3.执行sql语句preparedStatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);}finally{//4.最后释放资源DBUtil.close(connection,preparedStatement,null);}}//查询所有博客public List<Blog> getBlogs() {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;//将查询到的博客放在链表当中List<Blog> blogList = new ArrayList<>();try {//1.建立连接connection = DBUtil.getConnection();//2.创建sql语句//按照发布的时间查询,先发布的在下面String sql = "select *from blog order by postTime desc";preparedStatement = connection.prepareStatement(sql);//3.执行sql语句resultSet = preparedStatement.executeQuery();//用一个循环来遍历结果while(resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));//对全文内容进行截取String content = resultSet.getString("content");if(content.length() > 100) {content = content.substring(0,100)+"...";}blog.setContent(content);blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));blogList.add(blog);}} catch (SQLException e) {throw new RuntimeException(e);}finally{//释放资源DBUtil.close(connection,preparedStatement,resultSet);}return blogList;}//查看全文,查询某一篇具体的博客public Blog getBlogById(int blogId) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "select *from blog where blogId = ?";preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1,blogId);preparedStatement.executeQuery();//此处只有一个查询结果,不需要用循环if(resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));return blog;}} catch (SQLException e) {throw new RuntimeException(e);}finally {//释放资源DBUtil.close(connection,preparedStatement,resultSet);}return null;}//删除某篇博客public void delete(int blogId) {Connection connection = null;PreparedStatement preparedStatement = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "delete from blog where blogId = ?";preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1, blogId);//3.执行sql语句preparedStatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, preparedStatement, null);}}
}

UserDAO:

package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class UserDAO {//根据userId获取到用户的信息public User getUserById(int userId) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "select *from user where userId = ?";preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1,userId);resultSet = preparedStatement.executeQuery();if(resultSet.next()) {User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {throw new RuntimeException(e);}finally{DBUtil.close(connection,preparedStatement,resultSet);}return null;}//根据username获取到用户的信息public User getUserByName(String userName) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "select *from user where username = ?";preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1,userName);resultSet = preparedStatement.executeQuery();if(resultSet.next()) {User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {throw new RuntimeException(e);}finally{DBUtil.close(connection,preparedStatement,resultSet);}return null;}
}

后面会有一些数据库的框架:MyBatis,MyBatisPlus

用来封装JDBC的代码,本质上就是自动生成JDBC的代码

1.获取博客列表页

在博客列表页加载的时候,通过ajax给服务器发送请求,从数据库拿到博客列表数据,显示到页面上。

1)约定前后端交互的接口

2)让浏览器给服务器发送请求

3)服务器处理上述请求

4)让前端代码处理上述响应的数据

构成html片段,显示到页面上

前端页面生成页面的方式有很多种,这里采用的是基于dom api(document object model)的方式

dom api是浏览器提供的标准的api(不属于任何的第三方框架和库)

类似于jdbc api

前端有一些框架和库

把dom和api进行了封装,用起来更简便

1.   querySelector:获取页面已有的元素

2.   createElement:创建新的元素

3.  .innerHtml:设置元素里的内容

4.  .className:设置元素的class属性

5.  appendChild: 把这个元素添加到另一个元素的末尾

html中显示 > 需要使用到&gt;

显示 < 需要使用到&lt;

html中的<>就是用这个构成 

当前代码存在的两个问题:

1.

这里的发布时间是时间戳,我们需要格式化时间

这样就行:

2.

2.博客详情页

1)约定前后端交互的接口

2)让前端代码通过ajax发送请求

我们发送请求的时候需要带有blogId,而blogId处于博客详情页的url中,

我们可以通过location.search拿到页面url中的query string

一个路径对应到一个servlet

我们使用的一个servlet处理两个请求

博客列表页不带query string

博客详情页带有query string

可以根据是否有query string来区分是哪种请求,返回不同的数据

(使用两个servlet也可以,只是需要设置不同路径)

3)让服务器处理请求

4.前端拿到响应之后,把响应的数据,构造成页面的html片段

5.虽然正文的内容已经显示出来(正文的md原始数据),但是博客网站,应该显示的是md渲染后的效果

通过第三方库(editor.md)可以实现:

class属性往往是与css搭配。id则是一个身份标识,并且是唯一的。

editormd是一个全局变量,依赖正确引入,变量直接使用。

把blog.content这里的md原始数据,渲染成html,放到id为content的div中

3.实现登录

在登录页面,在输入框中填写用户名和密码

点击登录,就会给服务器发起HTTP请求(可以使用ajax,也可以使用form)

服务器处理登录请求。读取用户名和密码。

在数据库中查询匹配,如果正确就登陆成功,创建会话,跳转到博客列表页(这里采用重定向)。

1)约定前后端交互的接口

2)让前端发送请求

3)让服务器处理请求

4.实现强制登录

在博客列表页,详情页,编辑页,判定当前用户是否已经登陆

如果没有登录,则强制跳转到登录页(登录之后才可以使用)

1)约定前后端交互的接口

2)前端发起请求

一个页面可以触发多个ajax请求,不会互相干预

因为我们想要让博客列表页,博客编辑页,博客详情页都有强制登陆

所以我们可以把一些公共的js代码单独提取出来,放到某个.js文件中

然后通过html中的script标签,来引用文件的内容

eg:

博客列表页的强制登陆:

博客详情页的强制登录:

3)服务器处理请求:

虽然登陆过了,一旦重新启动服务器

就会被判定为未登录状态

登陆状态是通过服务器这里的session存储的

session这是服务器内存中的类似于hashmap这样的结构

一旦服务器重启了,hashmap原有的内容就没了

有更好的解决方案:

1.可以将会话进行持久化保存(文件,数据库,redis)

可以使用令牌的方式(把用户信息在服务器加密,还是保存在浏览器这边),相当于服务器没有在内存中存储当前用户的身份。

5.显示用户信息

博客列表页:显示的是当前登录的用户的信息

博客详情页:显示的是当前文章的作者信息

页面加载的时候,给服务器发送ajax请求,服务器返回对应的数据

1)约定前后端交互的接口

博客列表页:

博客详情页:

2)前端发送请求:

博客列表页:

博客详情页:

3)服务器处理请求:

博客列表页:

博客详情页:

这里我们查询分为了两步,先查blog表里的blog对象,再查user表

实际上可以使用联合查询,把blog user进行笛卡尔积,找出匹配的结果(可是开销大)

也可以使用子查询,把两个sql合并成一个(可读性查)

4)前端处理响应

6.退出登录

当点击注销(a标签,可有一个href属性)的时候,触发一个HTTP请求(GET),可能会引起浏览器的跳转

服务器收到GET请求时,就会把会话里的user这个Attribute给删掉

因为判断用户是否登录时,需要同时验证:会话存在,user的Attribute也许需要存在

破坏一个就可以使登陆状态发生改变

为什么不直接删除掉session?

:servlet没有提供删除session的方法

(虽然可以给session设置过期时间,起到删除的时间)

1)约定前后端交互的接口

2)前端发送请求:

3)后端处理请求:

7.发布博客

提交数据(title和content)

服务器存入数据库

1)约定前后端交互的接口

请求:

这里使用ajax,或者form(填写输入框,提交数据,使用form更方便)

2)前端发送请求

editor md 和form表单配合使用

初始化editor md 的编辑器的代码

3)服务器处理请求

 

出现问题时,如何解决?

比如:进入博客列表页,发现页面中的数据没有加载,该咋办?

抓包!!!

需要明确在点击刷新的过程中,浏览器个服务器之间有几次http交互(也就是前后端交互的接口)

之后就是观察抓包结果中,http交互的请求都是否符合预期

1.先看请求发没发

比如ajax写的方法是否调用了

2.再看请求中的各个部分对不对,是否符合约定的接口要求。

不符合则是前端代码的问题

3.请求没问题,看响应的数据

是否返回?数据库的语句,数据库的连接,服务器控制台是否异常

4.请求和响应都没问题,但是未显示。

前端处理响应,可以F12看控制台是否报错。

确定范围之后,进一步排查问题,加入更多日志。

System.out.println

Console.log


http://www.hkcw.cn/article/xyHeoJJGts.shtml

相关文章

街坊打麻将赌博被拘留 ​广州一直在重拳出击打麻将赌博行为

广州一直在重拳出击打麻将赌博行为。一黄埔街坊说自己因打麻将而被依法拘留5天罚款500元,下面评论并不多,仍看见多位广州ip地址的条友声称自已有同样或类似遭遇。有打麻将关十天的,有关两天罚五百的,有关半个月罚一千的。虽然无法证实或证伪,但也可以供参考一二。我认识的…

领域驱动设计(Domain-Driven Design, DDD)

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

江科大RTC实时时钟hal库实现

首先&#xff0c;因为的LSE无法启振&#xff0c;所以我使用LSI当作RTCCLK,LSI无法由备用电源供电&#xff0c;故主电源掉电时&#xff0c;RTC走时会暂停。 hal库相关函数 时分秒 typedef struct {uint8_t Hours; /*!< Specifies the RTC Time Hour.This param…

从“人防”到“智防”,智驱力助力危化品企业智能化转型

化工和危险化学品企业一直是国家安全生产监管的重点领域之一。近年来&#xff0c;随着AI技术的快速发展&#xff0c;越来越多的传统工业场景开始引入人工智能技术&#xff0c;实现从“人防”向“技防”的转变。多地应急管理厅也相继出台相关政策&#xff0c;推动视频智能分析系…

Nacos 2.4.3 登录配置

1&#xff0c; 调整配置 完整配置文件 # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy…

Cursor 0.51 全网首歌新功能深度体验:Generate Memories 让 AI 编程助手拥有“记忆“

写在前面 你是否遇到过这样的困扰:每次开启新的 Cursor 对话,都要重新向 AI 解释项目背景、技术栈、架构设计?或者当对话内容过多时,AI 就"忘记"了之前讨论的重要信息? 如果你有过这样的经历,那么 Cursor 0.51 版本新推出的 Generate Memories 功能绝对是你的…

如何在WHM中配置远程数据库访问

在远程数据库访问方面&#xff0c;cPanel和WHM之间存在一些差异&#xff1a; 在WHM中添加的主机将应用于所有cPanel用户帐户及其关联的MySQL用户。 cPanel用户无法永久删除由系统管理员root账户在WHM中添加的主机。 要允许远程主机访问MySQL数据库&#xff0c;请导航到侧边栏…

强大的PDF编辑工具,操作方便 ,长久使用

这是一款能够让每一个用户都能在这里轻松实现最简单的编辑方式&#xff0c;一站式完成PDF文件处理&#xff0c;较于前面几个版本&#xff0c;这个版本整体界面比较清爽&#xff0c;用户可以在这里一站式完成PDF编辑&#xff0c;在这里快速修改&#xff0c;编辑、创建、电子签名…

上海迪士尼游客打架官方通报 拍照冲突致肢体冲突

昨天21:45,浦东公安分局发布微博称,5月31日18时许接到报警,迪士尼乐园内发生打架事件。经初步调查,闫某某(男,22岁)与女友在拍照时,因刘某某(男,36岁)夫妻的女儿进入拍摄画面,双方发生口角后引发肢体冲突,造成闫某某和刘某某互有皮外伤,小女孩未受伤。目前,调查…

蔚来5月交付23231辆 同比增长13.1%

6月1日,蔚来公司公布了5月份的交付数据。当月共交付新车23231台,同比增长13.1%。具体来看,蔚来品牌交付了13270台;乐道品牌交付了6281台;firefly萤火虫品牌交付了3680台。截至目前,蔚来公司累计交付新车总数达到760789台。其中,蔚来品牌累计交付710655台;乐道品牌累计交…

沈白高铁进入开通运营倒计时 联调联试启动

6月1日,国家“十四五”规划重点高速铁路建设项目——沈阳至长白山高速铁路正式启动联调联试,标志着沈白高铁进入开通运营的倒计时。沈白高铁起自沈阳北站,途经辽宁省沈阳市、沈抚新区、抚顺市,以及吉林省通化市、白山市、延边州、长白山管委会,最终引入敦化至白河铁路长白…

台艺人刘乐妍称自己是中国人:反对"台独"是世界潮流,也是台湾唯一出路

六一发文称自己是中国人!台湾艺人刘乐妍:反对"台独"是世界潮流,也是台湾唯一出路!责任编辑:zx0002

Cesium使用primitive添加点线面(贴地)

// 创建一个图元集合const primitives viewer.scene.primitives.add(new Cesium.PrimitiveCollection());1、点上图 // 定义点的位置&#xff08;中国不同城市的经纬度&#xff09;const points [{ lon: 116.4074, lat: 39.9042, name: "北京" },{ lon: 121.4737, …

杨明洋社媒晒国足训练照 备战印尼全力以赴

国脚杨明洋更新了社交媒体,分享了自己随国足备战与印尼比赛的照片。国足目前在上海进行最后两场18强赛的备战工作,并计划于今日启程前往雅加达。杨明洋表示,首次入选国家队感到非常荣幸,能够成为其中一员对他来说意义重大。责任编辑:zx0176

警方通报上海迪士尼打架事件 拍照争执引发冲突

6月1日,浦东公安分局接到报警称,5月31日18时许迪士尼乐园内有人打架。经初步调查,闫某某(男,22岁)与女友在拍照时,因刘某某(男,36岁)夫妻的女儿进入拍摄画面,双方发生口角后引发肢体冲突,造成闫某某和刘某某互有皮外伤,小女孩未受伤。目前,调查处理工作正在进一步…

机器视觉图像处理之图像滤波

1.均值滤波 均值滤波是一种基本的线性图像滤波方法&#xff0c;主要用于图像平滑和噪声去除。 特点 优点&#xff1a; 算法简单&#xff0c;计算速度快 对高斯噪声有较好的去除效果 能有效平滑图像中的小波动 缺点&#xff1a; 会导致边缘模糊 对椒盐噪声(脉冲噪声)效果不…

8、电解电容—数据手册解读

目录 1、寿命 2、Rated Voltage 额定电压(VR) 3、Capacitance 额定容量(CR) 4、Surge Voltage 浪涌电压(VS) 5、Leakage Current 泄漏电流(ILC) 6、Dissipation Factor (Tanδ)损失角正切值 7、Ripple Current 纹波电流(IRC, rms) 8、参数特性 9、寿命计算 9.1 以江海为例&…

Python----目标检测(《YOLOv3:AnIncrementalImprovement》和YOLO-V3的原理与网络结构)

一、《YOLOv3:AnIncrementalImprovement》 1.1、基本信息 标题&#xff1a;YOLOv3: An Incremental Improvement 作者&#xff1a;Joseph Redmon, Ali Farhadi 机构&#xff1a;华盛顿大学&#xff08;University of Washington&#xff09; 发表时间&#xff1a;2018年 代…

ck-editor5的研究 (5):优化-页面离开时提醒保存,顺便了解一下 Editor的生命周期 和 6大编辑器类型

前言 经过前面的 4 篇内容&#xff0c;我们已经慢慢对 CKEditor5 熟悉起来了。这篇文章&#xff0c;我们就来做一个优化&#xff0c;顺便再补几个知识点&#xff1a; 当用户离开时页面时&#xff0c;提醒他保存数据了解一下 CKEditor5 的 六大编辑器类型了解一下 editor 实例对…

unity开发棋牌游戏

使用unity开发的棋牌游戏&#xff0c;目前包含麻将、斗地主、比鸡、牛牛四种玩法游戏。 相关技术 客户端&#xff1a;unity 热更新&#xff1a;xlua 服务器&#xff1a;c Web服务器&#xff1a;ruoyi 游戏视频 unity开发棋牌游戏 游戏截图