【JavaEE】前后端分离实现博客系统(后端实现)
创始人
2025-05-29 00:21:32
0

写在前面

 Hello,在上一篇中,我们已经实现了对于博客系统的页面构建任务。本次主要解决的问题就是针对这四个界面,实现后端的 servlet 程序,规范前后端交互的接口,编写客户端和服务端代码,处理请求并反馈。博客系统的完整代码已上传至 gitee,见文末链接。
 博客系统的页面构建:【JavaEE】前后端分离实现博客系统(页面构建)
在这里插入图片描述


文章目录

  • 写在前面
  • 1 MVC模式与前期准备
  • 2 model 层实现
    • 2.1 数据库的设计
    • 2.2 数据库表对应的实体类实现
    • 2.3 JDBC 工具类实现
    • 2.4 UserDao 的实现
    • 2.5 BlogDao 的实现
  • 3 controller 层实现
    • 3.1 博客列表页
    • 3.2 博客详情页
    • 3.3 登录功能
    • 3.4 检查用户的登录状态
    • 3.5 显示用户信息
    • 3.6 注销功能
    • 3.7 发布博客功能
    • 3.8 删除博客功能
  • 写在最后


1 MVC模式与前期准备

 MVC(Model View Controller)是一种软件设计的框架模式,它采用模型(Model)-视图(View)-控制器(controller)的方法把业务逻辑、数据与界面显示分离。把众多的业务逻辑聚集到一个部件里面,当然这种比较官方的解释是不能让我们足够清晰的理解什么是MVC的。用通俗的话来讲,MVC的理念就是把数据处理、数据展示(界面)和程序/用户的交互三者分离开的一种编程模式。
在这里插入图片描述
 而在本文所讲解的博客系统中,所谓的前后端分离,正是采用了这种形式。项目目录分层如下:controller层主要实现人机交互,将用户输入的指令和数据传递给model层,而model层主要包含数据表对应的实体类以及封装了对数据库的基本操作。

在这里插入图片描述
 首先,我们需要创建一个 maven 项目,并在pom.xml文件进行相应的配置。主要是 servlet、jdbc以及jackson。

pom.xml


4.0.0groupIdBlogSystem1.0-SNAPSHOT88javax.servletjavax.servlet-api3.1.0providedmysqlmysql-connector-java5.1.47com.fasterxml.jackson.corejackson-databind2.12.6.1

2 model 层实现

2.1 数据库的设计

 在博客系统中,主要包含登录功能、注销功能、发布博客、删除博客、博客展示的功能。涉及到的实体即博客和用户。数据库表设计如下,具体见代码注释:

db.sql

create database if not exists blog_system;use blog_system;-- 创建一个博客表
drop table if exists blog;
create table blog (blogId int primary key auto_increment, -- 文章idtitle varchar(1024), -- 文章标题content mediumtext, -- 文章内容userId int,  -- 文章作者idpostTime datetime -- 发布时间
);-- 创建一个用户表
drop table if exists user;
create table user (userId int primary key auto_increment,username varchar(128) unique,password varchar(128)
);-- 给 user表 插入一些数据
insert into user values(null, 'pwq', '7777');
insert into user values(null, 'hxh', '7777');-- 给 blog表 插入一些数据方便测试
insert into blog values(null, 'Java从入门到精通', '什么是Java?', 1, now());
insert into blog values(null, 'C++到底能做什么?', 'C++是什么?', 1, now());
insert into blog values(null, 'Python为什么那么火?', '我怎么知道?', 2, now());

2.2 数据库表对应的实体类实现

User类

每个 user 对象,对应 user 表的一条记录。

public class User {private int userId = 0;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;}
}

Blog类

每个 blog 对象,对应 blog 表的一条记录。

import java.sql.Timestamp;
import java.text.SimpleDateFormat;/*** @author 兴趣使然黄小黄* @version 1.0* 每个 model.Blog 对象对应 model.Blog 表中的一条记录*/
@SuppressWarnings({"all"})
public class Blog {private int blogId;private String title;private String content;private int userId;private Timestamp postTime;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 int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}//    public Timestamp getPostTime() {
//        return postTime;
//    }public String getPostTime(){// 这里对原先的 getPostTime() 进行修改, 将时间戳转化成格式化字符串并返回SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(postTime);}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}
}

针对时间戳在后端进行格式化字符串处理,便于前端的展示。
在这里插入图片描述

2.3 JDBC 工具类实现

  DBUtil 封装了用于获取数据库连接和关闭数据库连接资源的方法,便于 各个实体的Dao 使用,降低代码冗余度。

DBUtil.java

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;/*** @author 兴趣使然黄小黄* @version 1.0* 用于连接数据库与关闭数据库连接的工具类*/
@SuppressWarnings({"all"})
public class DBUtil {private static final String URL = "jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false";private static final String USERNAME = "root";private static final String PASSWORD = "xxxxxx";private static volatile DataSource dataSource = null;private static DataSource getDataSource(){if (dataSource == null){synchronized (DBUtil.class){if (dataSource == null){dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setURL(URL);((MysqlDataSource)dataSource).setUser(USERNAME);((MysqlDataSource)dataSource).setPassword(PASSWORD);}}}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) {e.printStackTrace();}}if (preparedStatement != null){try {preparedStatement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

2.4 UserDao 的实现

 该类封装了对于 User 表的操作,包括根据用户名或者用户ID返回 user对象。主要用于登录功能与在博客详情页和列表页展示用户信息。具体代码如下:

UserDao.java

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** @author 兴趣使然黄小黄* @version 1.0* 封装 User表 相关操作*/
@SuppressWarnings({"all"})
public class UserDao {/*** 登录功能实现,根据用户名来查找用户信息* @param username 用户名* @return 返回用户对象*/public User selectByName(String username){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from user where username = ?";statement = connection.prepareStatement(sql);statement.setString(1, username);resultSet = statement.executeQuery();// user 表中 username 使用 unique 约束,所以结果要么只有一条,要么没有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) {e.printStackTrace();} finally {DBUtil.close(connection, statement, resultSet);}return null;}/*** 主要用于博客详情页,根据用户 id 来查找用户信息, 将作者的名字显示出来* @param userId 用户 id* @return 返回用户对象*/public User selectById(int userId){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from user where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, userId);resultSet = statement.executeQuery();// user 表中 userId 使用 primary key 约束,所以结果要么只有一条,要么没有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) {e.printStackTrace();} finally {DBUtil.close(connection, statement, resultSet);}return null;}
}

2.5 BlogDao 的实现

 该类封装了有关 blog 表的操作。包括插入博客,返回博客列表,返回单一一条博客以及删除博客功能。具体见代码与注释:

BlogDao.java

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;/*** @author 兴趣使然黄小黄* @version 1.0* 封装 Blog表 相关操作*/
@SuppressWarnings({"all"})
public class BlogDao {/*** 插入博客* @param blog*/public void insert(Blog blog){Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();String sql = "insert into blog values(null, ?, ?, ?, now())";statement = connection.prepareStatement(sql);statement.setString(1, blog.getTitle());statement.setString(2, blog.getContent());statement.setInt(3, blog.getUserId());statement.executeUpdate();} catch (SQLException e) {e.printStackTrace();} finally {DBUtil.close(connection, statement, null);}}/*** 能够获取博客表中所有博客的信息(用于博客列表页展示摘要)* @return 返回博客列表*/public List selectAll(){List blogs = new ArrayList<>();Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from blog order by postTime desc";preparedStatement = connection.prepareStatement(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() > 50){content = content.substring(0, 50) + "...";}blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getInt("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}} catch (SQLException e) {e.printStackTrace();} finally {DBUtil.close(connection, preparedStatement, resultSet);}return blogs;}/*** 能够根据 博客id 返回一篇具体的博客* @param blogId 博客id* @return 返回具体的博客*/public Blog selectOne(int blogId){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);resultSet = statement.executeQuery();if (resultSet.next()){// 因为是根据主键查询, 所以结果只有一个, 使用 if 即可Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getInt("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));return blog;}} catch (SQLException e) {e.printStackTrace();} finally {DBUtil.close(connection, statement, resultSet);}return null;}/*** 根据博客 id 从博客表中删除博客* @param blogId 博客id*/public void delete(int blogId){Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();String sql = "delete from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);statement.executeUpdate();} catch (SQLException e) {e.printStackTrace();} finally {DBUtil.close(connection, statement, null);}}
}

3 controller 层实现

  无论是博客详情页、博客列表页还是登录功能诸如此类,其核心逻辑都是一样的,具体分为如下几个步骤:

  1. 约定前后端交互的接口;
  2. 实现服务器代码,分别为controller层的servlet实现的api,以及model层使用jdbc来操作数据库;
  3. 实现客户端代码:form / ajax / a标签跳转等。

在这里插入图片描述

3.1 博客列表页

 该页面用于展示数据库中的博客列表。约定请求:GET/blog,响应为 json 格式。

核心代码如下:

    private ObjectMapper objectMapper = new ObjectMapper();/*** 用于获取数据库中的博客列表、博客详情* @param req* @param resp* @throws ServletException* @throws IOException*/@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json; charset=utf8");BlogDao blogDao = new BlogDao();// 尝试获取请求中的 blogId, 根据结果区分访问详情页还是博客列表String param = req.getParameter("blogId");if (param == null){// 访问博客列表// 从数据库中直接查询博客列表, 然后转成 JSON格式 返回即可List blogs = blogDao.selectAll();// 把 blogs 对象转成 JSON 格式String respJson = objectMapper.writeValueAsString(blogs);resp.getWriter().write(respJson);} else {int blogId = Integer.parseInt(param);Blog blog = blogDao.selectOne(blogId);
//            System.out.println(blog.getContent());String respJson = objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}

编写客户端代码:

 在页面加载的时候,通过ajax获取数据,并构造标签,挂在dom树上,显示出来。

核心代码如下: