我们的目标是实现一个带有服务器版本的博客系统(重点关注后端开发)
1.准备工作
1)创建web项目
2)创建相应的目录结构、
3)配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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>blog_system</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.15.0</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>
<!-- 指定压缩包的格式 -->
<packaging>war</packaging>
<build>
<!-- 指定war包的名称 -->
<finalName>BlogSystem</finalName>
</build>
</project>
2.前端代码
2.1创建相关的目录
2.2每个目录或文件的内容
我们查看我的博客根据相关路径创建相应的文件,复制相应的内容即可,所需要复制的内容只有上述标记的即可。
2.3editor
editor存放的是markdown编辑器的内容,主要用于编写相应的博客。这个我们下载相应的代码即可。
- Editor.md - 开源在线 Markdown 编辑器 (pandao.github.io) 打开相应的网址
- 点击下载安装,然后点击使用giteehub下载
- 安装好后,我们有相应的压缩包,然后解压缩,把总文件夹相应的名字修改成一下,把他放到相应的目录下面即可
2.4页面效果预览
【登录页面】
【博客列表页】
【博客编辑页面】
【博客详情页】
3.数据库设计
3.1表的设计
博客系统主要有两个重要数据,一个博客数据,一个是用户数据。所以我们需要设计两张表,一张是文章表,一张是用户表
【文章表】文章表所需要的元素有 博客id,博客标题,博客正文,博客作者id,发布时间
【用户表】用户表所需要的内容有 用户id,用户名,用户密码
【完整的SQL语句】
我们可以在src/main目录下,创建一个db.sql,用于记录创建数据库时的语句,这样在部署到不同的服务器时,我们只需要复制一下即可
-- 一班对于建表的 sql 都会单独搞一个文件, .sql文件来保存
-- 后续程序可能需要在不同的主机上进行部署,部署的时候就需要在对应的主机上把数据库也创建好。
-- 把建表 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,
title varchar(20),
content varchar(4096),
userId int,
postTime datetime);
-- 默认先删除一下残留的数据
drop table if exists user;
-- 创建用户表
create table user(
userId int primary key auto_increment,
username varchar(50) unique,
password varchar(50));
-- 用于测试使用的些许数据
-- 用户数据
insert into user values(null, 'zhangsan', '123'), (null, 'lisi', '123');
-- 博客数据
insert into blog values(null, '我的第一篇博客', '这是第一篇博客的内容。', 1, '2023-06-04 12:00:00');
insert into blog values(null, '我的第二篇博客', '这是第二篇博客的内容。', 1, '2023-06-05 12:00:00');
4.封装数据库操作代码
我们发现使用JDBC进行数据库操作的时候,很多步骤都是固定的,如设置URL,设置用户,设置密码,建立连接,关闭资源等操作,我们将这些步骤提取出来以简化代码。
4.1创建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;
/**
* Describe:封装数据库操作代码
* User:lenovo
* Date:2023-07-03
* Time:15:29
*/
public class DBUtil {
//使用单例模式
private static volatile DataSource dataSource= null;
private static DataSource getDataSource() {
//这里使用一个if会每次都要加锁,使用两个if不用每次都加锁,同时用能判断dataSource == null
if(dataSource == null) {
synchronized(DBUtil.class) {
if(dataSource == null) {
dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("1020118096");
}
}
}
return dataSource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) throws SQLException {
if(connection != null) {
connection.close();
}
if(statement != null) {
statement.close();
}
if(resultSet != null) {
resultSet.close();
}
}
}
4.2创建Blog类和User类
【Blog】
package model;
import java.sql.Timestamp;
/**
* Describe:表示一篇博客的类
* User:lenovo
* Date:2023-07-03
* Time:15:47
*/
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 String getPostTime() {
//我们可以打印一下原来的类型,看看到Java中是什么样子
System.out.println(postTime);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return format.format(postTime);
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
}
【User】
package model;
/**
* Describe:表示用户相关信息的类
* User:lenovo
* Date:2023-07-03
* Time:15:51
*/
public class User {
private int userId;
private String username;
private String password;
//自动生成相应的get和set方法
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;
}
}
4.3创建BlogDao类和UserDao类
什么是"DAO"?
DAO的全程是“data access object"(数据访问对象),主要功能就是对某个数据库表进行增删查改。一般每张数据库表会对应一个DAO类。这是一种给类命名的习惯做法,并不是强制要求的。
创建BlogDao类,针对博客表进行操作
- insert:插入一个Blog对象到blog表种
- selectAll:从blog表中查找所有的Blog对象
- selectOne:从blog表中查找指定的Blog对象
- delete:从blog表中删除指定的Blog对象
package model;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Describe:对一个博客进行操作
* User:lenovo
* Date:2023-07-03
* Time:16:09
*/
public class BlogDao {
//把一个Blog对象插入到数据库中
public void insert(Blog blog) throws SQLException {
Connection connection = null;
PreparedStatement statement = null;
try {
//1.建立连接
connection = DBUtil.getConnection();
//2.构造SQL语句
String sql = "insert into blog values(null, ?, ?, ?, ?)";
statement = connection.prepareStatement(sql);
statement.setString(1, blog.getTitle());
statement.setString(2, blog.getContent());
statement.setInt(3, blog.getUserId());
statement.setString(4, blog.getPostTime());
//执行sql语句
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, null);
}
}
//查询 blog 表中所有博客数据
public List<Blog> selectAll() throws SQLException {
List<Blog> blogs = new ArrayList<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from blog order by postTime desc";
statement = connection.prepareStatement(sql);
resultSet = statement.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.setUserId(resultSet.getInt("userId"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
blogs.add(blog);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return blogs;
}
//指定博客ID来查询对应的博客
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()) {
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();
}
return null;
}
//指定 博客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();
}
}
}
创建UserDao类,实现对用户表的增删查改。
package model;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Describe:对用户进行操作
* User:lenovo
* Date:2023-07-04
* Time:10:18
*/
public class UserDao {
//通过ID查询
public User selectUserById(int userId) throws SQLException {
User user = null;
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();
if(resultSet.next()) {
user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return user;
}
//通过名字查询
public User selectUserByName(String name) throws SQLException {
User user = null;
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, name);
resultSet = statement.executeQuery();
if(resultSet.next()) {
user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return user;
}
}
5.创建相应的API
5.1创建BlogServlet.java
这个主要用于查看博客列表,查看博客详情,以及提交博客使用
package api;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;
/**
* Describe:通过这个类实现一些后端接口,这个主要用于查看博客列表,查看博客详情,以及提交博客使用
* User:lenovo
* Date:2023-07-08
* Time:10:58
*/
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从query string 中查询是否有 blogId,如果有就认为是查询指定的博客;如果没有就是查询所有博客
BlogDao blogDao = new BlogDao();
String blogId = req.getParameter("blogId");
System.out.println("这里在查看参数");
if(blogId == null) {
System.out.println("URL后面没有参数,表示查找全部博客");
List<Blog> blogs = null;
try {
blogs = blogDao.selectAll();
} catch (SQLException e) {
e.printStackTrace();
}
String respString = objectMapper.writeValueAsString(blogs);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(respString);
}else {
Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
String respString = objectMapper.writeValueAsString(blog);
resp.setContentType("application/json; charset=utf8");
resp.getWriter().write(respString);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("utf8");
// 1.先从请求中拿到标题和正文
String title = req.getParameter("title");
String content = req.getParameter("content");
if (title == null || title.equals("") || content == null || content.equals("")) {
String html = "<h3>title 或者 content 为空!新增博客失败!</h3>";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
// 2.从会话中拿到作者的id
HttpSession session = req.getSession(false);
if(session == null) {
String html = "<h3>当前用户为登录!新增博客博客失败!</h3>";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
User user = (User)session.getAttribute("user");
if(user == null) {
String html = "<h3>当前用户未登录!新增博客失败!<h3>";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
// 3.构造blog对象
Blog blog = new Blog();
blog.setUserId(user.getUserId());
blog.setTitle(content);
blog.setContent(content);
blog.setPostTime(new Timestamp(System.currentTimeMillis()));
// 4.插入blog对象到数据库中
BlogDao blogDao = new BlogDao();
try {
blogDao.insert(blog);
} catch (SQLException e) {
e.printStackTrace();
}
// 5.跳转到博客列表页
resp.sendRedirect("blog_list.html");
}
}
5.2创建LoginServlet.java
这个类主要用于匹配登录,并验证是否登录的,如果没有登录却在访问其他选项,会跳转到登录页面。
package api;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
import model.UserDao;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
/**
* Describe:登录的一些接口
* User:lenovo
* Date:2023-07-09
* Time:19:08
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException{
req.setCharacterEncoding("utf8");
// 1.从请求中,获取到用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
if(username == null || username.equals("") || password == null || password.equals("")) {
// 用户名或密码残缺
String html = "<h3>登录失败!缺少用户名或者密码!</h3>";
resp.setContentType("text/html; charset=uft8");
resp.getWriter().write(html);
return;
}
// 2.从数据库中读数据,看看用户名和密码是否匹配
UserDao userDao = new UserDao();
User user = null;
try {
user = userDao.selectUserByName(username);
} catch (SQLException e) {
e.printStackTrace();
}
if(user == null) {
// 用户不存在
String html = "<h3>用户名或密码错误!</h3>";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
if(!password.equals(user.getPassword())) {
// 密码错误
String html = "<h3>用户名或密码错误!</h3>";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
// 3.用户名和密码都正确,登录成功,需要设置会话
HttpSession session = req.getSession(true);
// 此处就把用户对象储存到session中了,下次用户访问其他页面,就可以直接拿到会话
session.setAttribute("user", user);
// 4.返回一个重定向相应,能够跳转到博客列表页
resp.sendRedirect("blog_list.html");
}
//通过这个方法,判断用户的登录状态,已登录,返回200,未登录返回403
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//查询会话是否存在
HttpSession session = req.getSession(false);
if(session == null) {
//会话不存在,未登录状态
resp.setStatus(403);
return;
}
User user = (User)session.getAttribute("user");
if(user == null) {
//会话虽然存在,但是用户对象没有,未登录状态
resp.setStatus(403);
return;
}
//已经登录了
//200是默认的状态码,此处不写200也是可以的
resp.setStatus(200);
resp.setContentType("application/json;charset=utf8");
user.setPassword("");//避免密码被返回,我们设置未空
String respJson = objectMapper.writeValueAsString(user);
resp.getWriter().write(respJson);
}
}
5.3创建UserServlet.java
这个类主要用于博客详情页,用于查询指定的博客对应的作者
package api;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import model.UserDao;
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.sql.SQLException;
/**
* Describe:
* User:lenovo
* Date:2023-07-10
* Time:10:26
*/
@WebServlet("/user")
public class UserServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.先读取出blogId
String blogId = req.getParameter("blogId");
if(blogId == null || blogId.equals("")) {
// 直接返回一个 userId 为0的对象,因为最终返回的是一个 json 数据
// 此处返回 json 格式的对象,如果返回一个html,前端处理会很麻烦
String respJson = objectMapper.writer().writeValueAsString(new User());
resp.setContentType("application/json; charset=utf8");
System.out.println("参数给定的blogId为空");
return;
}
// 2.查询数据库,查询对应的 Blog 对象
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
if(blog == null) {
String respJson = objectMapper.writeValueAsString(new User());
resp.setContentType("application/json; charset=utf8");
resp.getWriter().write(respJson);
System.out.println("阐述给定的blogId不存在!");
return;
}
// 3.根据 blog 中 userId,查询作者的信息
UserDao userDao = new UserDao();
User user = null;
try {
user = userDao.selectUserById(blog.getUserId());
if(user == null) {
String respJson = objectMapper.writeValueAsString(new User());
resp.setContentType("application/json; charset=utf8");
resp.getWriter().write(respJson);
System.out.println("该博客对应的作者不存在!");
return;
}
} catch (SQLException e) {
e.printStackTrace();
}
// 4.把user对象返回给页面
String respJson = objectMapper.writeValueAsString(user);
resp.setContentType("application/json; charset=utf8");
resp.getWriter().write(respJson);
}
}
5.4创建LogoutServlet.java
用于注销账号
package api;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Describe:用于注销功能
* User:lenovo
* Date:2023-07-10
* Time:10:56
*/
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
HttpSession session = req.getSession(false);
if(session == null) {
//本身就没有登陆
resp.sendRedirect("login.html");
return;
}
//如果user存在
session.removeAttribute("user");
resp.sendRedirect("login.html");
}
}
6.总结
这是一个简单的博客系统,我们完成了博客登录页,博客详情页,博客列表页,博客编辑页,注销功能。
这个对于初学者先对比较难,也会发生很多的问题。我们可以通过在控制台打印的方式来确定问题的位置,如
当然在我们上线服务器的时候,只要进行注释即可