我们的目标是实现一个带有服务器版本的博客系统(重点关注后端开发)
1)创建web项目
2)创建相应的目录结构、
3)配置pom.xml
4.0.0
org.example
blog_system
1.0-SNAPSHOT
8
8
javax.servlet
javax.servlet-api
3.1.0
provided
com.fasterxml.jackson.core
jackson-databind
2.15.0
mysql
mysql-connector-java
5.1.49
war
BlogSystem
我们查看我的博客根据相关路径创建相应的文件,复制相应的内容即可,所需要复制的内容只有上述标记的即可。
editor存放的是markdown编辑器的内容,主要用于编写相应的博客。这个我们下载相应的代码即可。
【登录页面】
【博客列表页】
【博客编辑页面】
【博客详情页】
博客系统主要有两个重要数据,一个博客数据,一个是用户数据。所以我们需要设计两张表,一张是文章表,一张是用户表
【文章表】文章表所需要的元素有 博客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');
我们发现使用JDBC进行数据库操作的时候,很多步骤都是固定的,如设置URL,设置用户,设置密码,建立连接,关闭资源等操作,我们将这些步骤提取出来以简化代码。
通过一个单例模式来获取数据库连接。
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();
}
}
}
【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;
}
}
什么是"DAO"?
DAO的全程是“data access object"(数据访问对象),主要功能就是对某个数据库表进行增删查改。一般每张数据库表会对应一个DAO类。这是一种给类命名的习惯做法,并不是强制要求的。
创建BlogDao类,针对博客表进行操作
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 selectAll() throws SQLException {
List 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;
}
}
这个主要用于查看博客列表,查看博客详情,以及提交博客使用
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 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 = "title 或者 content 为空!新增博客失败!
";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
// 2.从会话中拿到作者的id
HttpSession session = req.getSession(false);
if(session == null) {
String html = "当前用户为登录!新增博客博客失败!
";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
User user = (User)session.getAttribute("user");
if(user == null) {
String html = "当前用户未登录!新增博客失败!";
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");
}
}
这个类主要用于匹配登录,并验证是否登录的,如果没有登录却在访问其他选项,会跳转到登录页面。
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 = "登录失败!缺少用户名或者密码!
";
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 = "用户名或密码错误!
";
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(html);
return;
}
if(!password.equals(user.getPassword())) {
// 密码错误
String html = "用户名或密码错误!
";
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);
}
}
这个类主要用于博客详情页,用于查询指定的博客对应的作者
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);
}
}
用于注销账号
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");
}
}
这是一个简单的博客系统,我们完成了博客登录页,博客详情页,博客列表页,博客编辑页,注销功能。
这个对于初学者先对比较难,也会发生很多的问题。我们可以通过在控制台打印的方式来确定问题的位置,如
当然在我们上线服务器的时候,只要进行注释即可