文章目录
- 效果展示
- 1. 创建 maven 项目
- 2. 设计数据库
- 3. 封装数据库的操作代码
- 3.1 创建 DBUtil 类
- 3.2 创建 Blog(代表一篇博客)
- 3.3 创建 User(代表一个用户)
- 3.4 创建类 BlogDao(对博客表进行操作)
- 3.5 创建类 UserDao (对用户表进行操作)
- 4. 导入之前已经写好的前端代码
- 5. 实现博客主页界面
- 5.1 约定好前后端交互接口
- 5.2 实现 BlogServlet
- 5.3 实现 前端代码
- 6. 实现博客详情界面
- 6.1 约定好前后端交互接口
- 6.2 实现BlogServlet
- 6.3 实现前端代码
- 7. 实现登录界面
- 7.1 约定好前后端交互接口
- 7.2 实现 LoginServlet
- 7.3 实现前端代码
- 8. 实现登录状态判定功能
- 8.1 约定前后端交互接口
- 8.2 在 LoginServlet 进行代码添加
- 8.3 在前端代码中创建 common.js
- 8.4 修改前端代码
- 9. 实现显示用户信息功能
- 9.1 约定好前后端交互接口
- 9.2 实现 AuthorServlet 代码
- 9.3 实现前端代码
- 针对博客列表页进行修改
- 针对博客详情页
- 10. 实现注销功能
- 10.1 约定好前后端交互接口
- 10.2 实现 LogouServlet
- 10.3 实现前端代码
- 11. 实现发布博客功能
- 11.1 约定好前后端交互的接口
- 11.2 在BlogServlet中添加doPost方法
- 11.3 实现前端代码
- 12. 删除博客
- 12.1 约定号前后端交互接口
- 12.1 实现前端代码
- 12.2 实现BlogDeleteServlet
效果展示
1. 创建 maven 项目
创建必要的目录,引入需要的依赖
2. 设计数据库
本系统要存入博客文章的信息和用户的信息
创建博客表:
博客的 id,博客的标题,博客的内容,博客的日期,博文的博主 id
创建用户表:
用户 id 用户名 用户密码
-- 创建一个数据库create database if not exists java102_blog;use java102_blog;-- 创建一个博客表.drop table if exists blog;create table blog (blogId int primary key auto_increment,title varchar(1024),content mediumtext,userId int, -- 文章作者的 idpostTime datetime -- 发布时间);---- 给博客表中插入点数据, 方便测试.insert into blog values(null, '这是第一篇博客', '从今天开始, 我要认真学 Java', 1, now());insert into blog values(null, '这是第二篇博客', '从昨天开始, 我要认真学 Java', 1, now());insert into blog values(null, '这是第三篇博客', '从前天开始, 我要认真学 Java', 1, now());insert into blog values(null, '这是第一篇博客', '从今天开始, 我要认真学 C++', 2, now());insert into blog values(null, '这是第二篇博客', '从昨天开始, 我要认真学 C++', 2, now());insert into blog values(null, '这是第三篇博客', '# 一级标题\n ### 三级标题\n > 这是引用内容', 2, now());-- 创建一个用户表drop table if exists user;create table user (userId int primary key auto_increment,username varchar(128) unique,-- 后续会使用用户名进行登录, 一般用于登录的用户名都是不能重复的.password varchar(128));insert into user values(null, 'zhangsan', '123');insert into user values(null, 'lisi', '123');insert into user values(null, 'ling', '123');
3. 封装数据库的操作代码
创建包 model 用来存放数据库的代码
3.1 创建 DBUtil 类
用于和数据库建立连接
package model;import com.mysql.cj.jdbc.MysqlDataSource;import javax.sql.DataSource;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;/** *这个类 封装 和 建立 数据库 连接 的 操作 */public class DBUtil {private static final String URL = "jdbc:mysql://127.0.0.1:3306/java102_blog" />
3.2 创建 Blog(代表一篇博客)
package model;import java.sql.Timestamp;import java.text.SimpleDateFormat;// 每个 blog对象 ,对应 blog 表里的一条记录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;}// 把这里 的 getter 方法给改了,不是返回一个 时间搓对象,而是返回一个 string(格式化的时间)public String getPostTime() {// 使用这个类 来完成时间戳到格式化日期的转换// 这个 转换过程,需要构造方法中制定要转换的格式,然后调用 format 来进行转换SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(postTime);}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}}
3.3 创建 User(代表一个用户)
package model;// 每个 model.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;}}
3.4 创建类 BlogDao(对博客表进行操作)
注意:
对数据进行插入删除操作时,执行 sql:
其他操作:
插入顺序:
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;/** * 这个类用于去封装博客表的 基本操作 */public class BlogDao {// 1.往博客表里,插入一个博客public void insert(Blog blog) {// JDBC 代码Connection connection = null;PreparedStatement statement = null;try {// 1. 先和数据库建立连接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "insert into blog values(null," /> selectAll() {List blogs = new ArrayList();// JDBC 代码Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {// 1. 先建立链接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "select * from blog order by postTime desc";statement = connection.prepareStatement(sql);// 3. 执行 sqlresultSet = statement.executeQuery();// 把查询到的数据 存储到 blogs 当中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(content);blog.setUserId(resultSet.getShort("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}} catch (SQLException throwables) {throwables.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return blogs;}// 3. 能够根据博客 id 获取到制定的博客内容(用于博客详情页)public Blog selectOne(int blogId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try{// 1. 建立连接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);// 3. 执行 sqlresultSet = statement.executeQuery();// 此处我们是使用 主键 来作为 查询条件,查询结果,要么是 1,要么是 0if(resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getShort("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));return blog;}} catch (SQLException throwables) {throwables.printStackTrace();}finally {// 4. 关闭连接 释放资源DBUtil.close(connection,statement,resultSet);}return null;}// 4.从博客列表中,根据博客 id 删除博客public void delete(int blogId) {Connection connection = null;PreparedStatement statement = null;try {// 1. 建立 连接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "delete from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);// 3. 执行 sqlstatement.executeUpdate();} catch (SQLException throwables) {throwables.printStackTrace();}finally {DBUtil.close(connection,statement,null);}}}
3.5 创建类 UserDao (对用户表进行操作)
package model;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;/** * 这个类用于 封装 用户表的 基本操作 */public class UserDao {// 1. 根据用户名 查找用户信息public User selectByName(String username) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try{// 1. 建立 连接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "select * from user where username = ?";statement = connection.prepareStatement(sql);statement.setString(1,username);// 3. 执行 sqlresultSet = statement.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 throwables) {throwables.printStackTrace();}finally {// 4. 关闭连接 释放资源DBUtil.close(connection,statement,resultSet);}return null;}// 2. 根据 用户 id 来找 用户信息// 博客详情页 就可以根据用户 id 来查询作者的名字,把作者名字显示出来public User selectById(int userId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {// 1. 建立连接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "select * from user where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,userId);// 3. 执行sqlresultSet = statement.executeQuery();// 此处的 username 使用 unique 约束,要么能查到一个,要么一个都查不到// 4. 遍历结果集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 throwables) {throwables.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}}
4. 导入之前已经写好的前端代码
5. 实现博客主页界面
5.1 约定好前后端交互接口
5.2 实现 BlogServlet
import model.BlogDao;import javax.servlet.ServletException;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.util.List;// 通过这个类, 来处理 /blog 路径对应的请求@WebServlet("/blog")public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();// 这个方法用来获取到数据库中的博客列表.@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 从数据库中查询到博客列表, 转成 JSON 格式, 然后直接返回即可.BlogDao blogDao = new BlogDao();List blogs = blogDao.selectAll(); //把 blogs 对象转成 JSON 格式.String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}}
5.3 实现 前端代码
在 blog_list.html 中 实现 ajax
注意:引入依赖
// 在页面加载的时候, 通过 ajax 给服务器发送数据, 获取到博客列表信息, 并且显示在界面上. function getBlogList() {$.ajax({type: 'get',url: 'blog',success: function(body) {// 获取到的 body 就是一个 js 对象数组, 每个元素就是一个 js 对象, 根据这个对象构造 div// 1. 先把 .right 里原有的内容给清空let rightDiv = document.querySelector('.right');rightDiv.innerHTML = '';// 2. 遍历 body, 构造出一个个的 blogDivfor (let blog of body) {let blogDiv = document.createElement('div');blogDiv.className = 'blog';// 构造标题let titleDiv = document.createElement('div');titleDiv.className = 'title';titleDiv.innerHTML = blog.title;blogDiv.appendChild(titleDiv);// 构造发布时间let dateDiv = document.createElement('div');dateDiv.className = 'date';dateDiv.innerHTML = blog.postTime;blogDiv.appendChild(dateDiv);// 构造博客的摘要let descDiv = document.createElement('div');descDiv.className = 'desc';descDiv.innerHTML = blog.content;blogDiv.appendChild(descDiv);// 构造 查看全文let a = document.createElement('a');a.innerHTML = '查看全文 >>';// 此处希望点击之后能够跳转到 博客详情页 !!// 这个跳转过程需要告知服务器要访问的是哪个博客的详情页. a.href = 'blog_detil.html" />
6. 实现博客详情界面
6.1 约定好前后端交互接口
6.2 实现BlogServlet
这里后端代码和博客列表页的获取,基本相同,就直接放到一个方法中,来实现!使用blogId参数来区别是获取博客列表还是详情
这里注意:博客列表页在也在BlogServlet中实现的,我们如何去区别呢?:
package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.Blog;import model.BlogDao;import javax.servlet.ServletException;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.util.List;/** * // doGet 获取博客列表页 */@WebServlet("/blog")public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json; charset=utf8");BlogDao blogDao = new BlogDao();// 先尝试获取到 req 中的 blogId 参数. 如果该参数存在, 说明是要请求博客详情// 如果该参数不存在, 说明是要请求博客的列表.String param = req.getParameter("blogId");if (param == null) {// 不存在参数, 获取博客列表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);String respJson = objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}}
6.3 实现前端代码
修改 blog_detail.html
,让这个页面加载的时候,能够调用到上述的接口,来从服务器获取到博客数据
注意:
在博客详情页中引入 editor
function getBlogDetail() {$.ajax({type: 'get',// location.search 拿到了形如 '" />h3");h3.innerHTML = body.title;// 2. 构造博客发布时间let dateDiv = document.querySelector('.date');dateDiv.innerHTML = body.postTime;// 3. 构造博客正文// 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串// 咱们需要的是渲染后的, 带有格式的效果// let content = document.querySelector('#content');// content.innerHTML = body.content;// 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下. editormd.markdownToHTML('content', {markdown: body.content});}});}getBlogDetail();
7. 实现登录界面
这里需要注意:啥样才算是已经登录
用户有一个 session,同时 session 有一个 user 属性 ~
两者同时具备,才叫登录状态
这里先给注销做铺垫:理解一下注销是如何操作的:
注销只要破坏掉上面的任意一个条件就行了
7.1 约定好前后端交互接口
7.2 实现 LoginServlet
package controller;import com.fasterxml.jackson.databind.ObjectMapper;import model.User;import model.UserDao;import javax.servlet.ServletException;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;// 实现登录页面@WebServlet("/login")public class LoginServlet extends HttpServlet {privateObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setCharacterEncoding("utf-8");// 1. 获取到请求中的参数String username = req.getParameter("username");String password = req.getParameter("password");if(username == null || "".equals(username) || password == null || "".equals(password)) {// 请求内容缺失,登录失败resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前的用户名或者密码为空");return;}// 2. 和数据库中的的内容进行比较UserDao userDao = new UserDao();User user = userDao.selectByName(username);if(user == null || !user.getPassword().equals(password)) {// 用户没有查到或者密码不匹配,登录失败resp.setContentType("text/html;charset=utf8");resp.getWriter().write("用户名或密码错误");return;}// 3. 如果比较通过,则创建会话HttpSession session = req.getSession(true);// 把刚才的用户信息,存储到会话中session.setAttribute("user",user);// 4. 返回一个重定向报文,跳转到博客列表页resp.sendRedirect("blog_list.html");} }
7.3 实现前端代码
修改blog_login.html
这里只需要在原来登录操作上套上一层 form 标签就可以了
因为这里在原有的基础上填上了 form表单,我们在 css 中也会有一些改动
登录页面 登录
用户名密码<!-- -->