【JavaWeb项目】博客系统

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 在网络学完HTTP协议,前端学完html,css,js,后端学完Servlet开发后,做一个博客系统,巩固一下所学知识,并将所学知识运用到实际当中,以此来进一步提升对学习编程的兴趣

1. 项目简介

1.1 项目背景

在网络学完HTTP协议,前端学完html,css,js,后端学完Servlet开发后,做一个博客系统,巩固一下所学知识,并将所学知识运用到实际当中,以此来进一步提升对学习编程的兴趣


1.2 项目用到的技术

前端使用到html,css,js,使用ajax技术发送http请求,请求body的格式为json格式

后端使用Servlet进行开发

使用Mysql数据库保存数据

除此还引入了editor.md,editor.md是一个开源的页面markdown编辑器组件

采用Maven构建工具搭建项目开发环境

1.3 项目功能简单介绍

登陆页面:输入用户及密码,点击提交,如果用户或密码错误,会提示用户或密码错误,账号及密码正确则登陆成功,成功后跳转到博客列表页面

博客列表页面:博客列表页面展示所有发布过的文章,文章显示最多显示50字,如果想查看全文,则需要点击文章下的显示全文

博客详情页面:点击某篇文章的显示全文按钮,则会展示文章的全部内容

博客编辑页面:点击博客列表的写博客,会跳转到博客编辑页面,输入文章题目及文章内容点击发布文章,文章即可发布成功,发布成功后会跳转到博客列表页面,可以查看发布过的文章

博客注销按钮:点击博客注销按钮,则会跳转到博客登陆页面

2. 页面及功能展示

登陆页面

微信图片_20221030172311.png


输入用户和密码,点击提交,登陆成功后跳转到博客列表页面

微信图片_20221030172327.png



点击写博客,跳转到博客编辑页面

微信图片_20221030172342.png



输入文章标题和文章内容,点击发布文章,发布成功后跳转到博客列表页面

微信图片_20221030172344.png



在博客列表页面,点击刚发布文章的显示全文,就会显示刚才发布文章的全部内容

微信图片_20221030172347.png



点击注销又跳转到博客登陆页面

微信图片_20221030172351.png

3. 博客系统页面设计

这里附上静态页面设计的码云地址,可以点击查看,本篇文章只展示后端代码与前端ajax交互的部分,想要查看博客系统页面设计代码,请点击:个人博客系统的页面设计代码


4. 项目准备工作

创建Maven项目在pom.xml中添加项目依赖


后端采用Servlet开发

数据库使用Mysql

jackson框架可以进行序列化和反序列化,将java对象和json字符串相互转化

junit框架提供单元测试

<?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 
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>my-blog</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.3</version>
        </dependency>
        <!--  单元测试框架      -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>my-blog</finalName>
    </build>
</project>

创建与开发相关的包


 image.png

引入前端资源


image.png

将前端资源都放在main/webapp目录下,前端资源从上面码云地址中获取,web.xml存放在main/webapp/WEB-INF目录下


5. 博客系统功能设计

5.1 设计数据库表

有用户登陆,所以有一张用户表,观察博客列表有显示用户昵称,所以用户表设计有四个字段:用户id,用户名,密码,昵称

有文章展示,所以有一张文章表,文章有文章id,标题,发布时间,文章内容,关联用户的外键

一个用户可以发布多篇文章,所以用户与文章对应关系为1:m,用户id作为文章表的外键

创建表的时候可以插入一些数据便于后续的测试

drop database if exists blog;
create database blog character set utf8mb4;
use blog;
create table user(
    id int primary key auto_increment,
    username varchar(20) not null unique,
    password varchar(20) not null,
    nickname varchar(10) not null
);
insert into user values(null,'abc','123','糯米');
create table article(
    id int primary key auto_increment,
    title varchar(50) not null,
    `date` date,
    content mediumtext,
    user_id int,
    foreign key (user_id) references user(id)
);
insert into article values(null,'文章1','2022-9-9','今天要好好学习',1);
insert into article values(null,'文章2','2022-9-17','今天要玩游戏',1);


5.2 工具类util

1. 创建数据库工具类DBUtil,提供获取数据库连接和统一释放资源

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
//数据库工具类,提供获取数据库连接,释放资源统一代码
public class DBUtil {
    //一个程序,连接一个数据库,只需要一个连接池,其中保存了多个数据库连接对象
    private static MysqlDataSource ds; //静态变量,类加载时执行初始化,只执行一次
    //获取连接池,内部使用,不开放
    private static DataSource getDataSource(){
        if(ds == null){
            ds = new MysqlDataSource();
            ds.setURL("jdbc:mysql://127.0.0.1:3306/blog");
            ds.setUser("root");
            ds.setPassword("xiaobai520..@@@");
            ds.setUseSSL(false); //不安全连接,不设置会有警告
            ds.setCharacterEncoding("UTF-8");
        }
        return ds;
    }
    //获取数据库连接对象,开放给外部的jdbc代码使用
    public static Connection getConnection(){
        try {
            return getDataSource().getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("获取数据库连接报错",e);
        }
    }
    //释放资源,查询操作需要释放三个资源
    public static void close(Connection c, Statement s, ResultSet rs){
        try {
            if(rs != null) rs.close();
            if(s != null) s.close();
            if(c != null) c.close();
        } catch (SQLException e) {
            throw new RuntimeException("释放数据库资源出错",e);
        }
    }
    //更新操作释放两个资源
    public static void close(Connection c,Statement s){
        close(c,s,null);
    }
}


2. 创建Web工具类WebUtil,提供一个类专门检查用户是否登陆,还提供序列化与反序列化类,用来将java对象与json字符串相互转化

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.model.User;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
public class WebUtil {
    public static User checkLogin(HttpServletRequest req){
        User user = null;
        HttpSession session = req.getSession(false);
        user = (User) session.getAttribute("user");
        return user;
    }
    //使用单例
    private static ObjectMapper mapper = new ObjectMapper();
    //反序列化:json字符串转换Java对象
    //使用泛型,传一个什么类型,就返回该类型的对象
    //泛型方法:方法限定符 <类型型参列表> 返回值类型 方法名
    public static <T> T read(InputStream is,Class<T> clazz){
        try {
            return mapper.readValue(is,clazz);
        } catch (IOException e) {
            throw new RuntimeException("json反序列化出错",e);
        }
    }
    //序列化:将java对象转化为json字符串
    public static String write(Object o){
        try {
            return mapper.writeValueAsString(o);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("json序列化出错",e);
        }
    }
}


5.3 实体类model

每张用户表对应有一个实体类,所以创建User和Article类,创建类的时候提供Getter和Setter方法并且重写toString方法


用户类User

public class User {
    private Integer id;
    private String username;
    private String password;
    private String nickname;
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    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;
    }
    public String getNickname() {
        return nickname;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
}


文章类Article

import java.util.Date;
public class Article {
    private Integer id;
    private String title;
    private Date date; //时间
    private String content;
    private Integer userId;
    private String dateString;//日期字符串
    @Override
    public String toString() {
        return "Article{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", date=" + date +
                ", content='" + content + '\'' +
                ", userId=" + userId +
                ", dateString='" + dateString + '\'' +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getDateString() {
        return dateString;
    }
    public void setDateString(String dateString) {
        this.dateString = dateString;
    }
}


Java对象JsonResult类


后端返回给前端的响应时返回的是json字符串,所以需要一个JsonResult类,该类的字段保存操作是否成功和要返回给前端的数据,后端在返回给前端json字符串的时候只需要将该类序列化为json字符串返回给前端,该类也得提供Getter与Setter方法并且重写toString方法

public class JsonResult {
    private boolean ok;//标识执行一个操作是否成功
    private Object data;//操作成功,且是一个查询操作,需要返回一些数据给前端
    @Override
    public String toString() {
        return "JsonResult{" +
                "ok=" + ok +
                ", data=" + data +
                '}';
    }
    public boolean isOk() {
        return ok;
    }
    public void setOk(boolean ok) {
        this.ok = ok;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
}

5.4 前后端业务处理

前后端业务逻辑的实现顺序:


先在前端构造ajax请求

再做后端业务逻辑

最后再前端才设置回调函数执行回调

说明:后面实现的顺序是先前端再后端,但是观看参考时,建议按照上面的逻辑顺序,因为实际开发时就是按照这个顺序来开发的


5.4.1 登陆页面功能设计

所有的请求都是使用ajax发送http请求,所以将封装的ajax函数写在一个js文件中,后续发送请求时只需将封装的ajax函数引入即可


封装的ajax函数

//封装ajax函数,args为一个js对象
//args对象属性如下:
//method:请求方法,url:请求资源路径,contenType:请求正文格式
//body:请求正文,callback:回调函数,客户端接收到响应数据后调用
function ajax(args) {
    let xhr = new XMLHttpRequest();
    //设置回调函数
    xhr.onreadystatechange = function () {
        //4:客户端接收到服务端响应
        if (xhr.readyState == 4) {
            //回调函数可能会使用响应的内容,作为传入参数
            args.callback(xhr.status, xhr.responseText);
        }
    }
    xhr.open(args.method, args.url);
    //如果args中contentType有内容,就设置Content-Type请求头
    if (args.contentType) {//js中if可以判断是否有值
        xhr.setRequestHeader("Content-Type", args.contentType);
    }
    //如果args中body有内容,设置body请求正文
    if (args.body) {
        xhr.send(args.body);
    } else {
        xhr.send();
    }
}


前端设计


给登陆提交按钮绑定点击事件,获取到输入的用户名和密码后,发送ajax请求

请求方法为post,请求url为login,后端的Servlet路径也要与此对应,设置contentType为application/json,请求body为json字符串

json对象保存输入的用户名和密码,将json对象转化为json字符串设置到body中

设置回调函数,回调函数有两个参数,一个为响应状态码,一个为后端返回的响应为json字符串,后端返回的json字符串中保存操作是否成功字段

如果响应状态码为200,并且ok为true,则登陆成功,跳转到博客列表页面,如果ok为false则用户名或密码错误,如果响应状态码不为200,则提示响应状态码及响应body,以便程序员作出更改

<script src="js/util.js"></script>
<script>
    let submit = document.querySelector("#submit");
    //绑定提交按钮点击事件
    submit.onclick = function(){
        let username = document.querySelector("#username").value;
        let password = document.querySelector("#password").value;
        //发送ajax请求,需要设置method,url,contentType,body
        ajax({
            method: "post",
            url: "login",
            contentType: "application/json",
            body: JSON.stringify({
                //冒号前是前后端约定的键,冒号后是变量值
                username: username,
                password: password
            }),
            callback: function(status,responseText){
                if(status == 200){
                    let json = JSON.parse(responseText);
                    if(json.ok){
                        alert("登陆成功");
                        window.location.href = "blog_list.html";
                    }else {
                        alert("账号或密码错误");
                    }
                }else {
                    alert("响应状态码:"+status+"/nbody:"+responseText);
                }
            }
        });
    }
</script>


后端Servlet设计


前端发送的body为json字符串

所以先使用InputStream输入流获取请求数据,将请求数据转化为user对象

使用该对象在数据库做校验,如果校验成功,设置session,设置要返回给前端的JsonResult对象json

设置完后,将json对象序列化json字符串后返回给前端

前端的回调函数就是依据后端返回的json字符串做相应的逻辑处理

@WebServlet("/login")//登陆
public class LoginServlet extends HttpServlet {
    //登陆功能,json提交{username:abc,password:123}
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解析请求:通过输入流获取请求数据
        req.setCharacterEncoding("utf-8"); //设置请求对象的编码格式
        InputStream is = req.getInputStream();
        //将输入流中的json字符串转化为java对象
        //使用ObjectMapper将java对象和json字符串相互转换,Servlet都要用,封装到WebUtil中
        User get = WebUtil.read(is,User.class);
        //在数据库校验账号密码:通过账号密码在数据库查用户,若能查到则账号密码正确
        User user = UserDao.isLogin(get.getUsername(),get.getPassword());
        //不管登陆是否成功,返回的http响应正文(body)都是json字符串
        //需要设计一个类,这个类的成员变量属性,用于前端ajax解析响应
        //先创建一个响应正文需要的Java对象,然后在转换为json字符串,再设置到响应正文
        JsonResult json = new JsonResult();
        if(user != null){
            //登陆成功,设置session
            HttpSession session = req.getSession(true);
            session.setAttribute("user",user);
            //设置json对象中,操作是否成功为true
            json.setOk(true);
        }else {
            //登陆失败,设置操作是否成功字段为false
            json.setOk(false);
        }
        //设置响应正文的格式
        resp.setContentType("application/json; charset=utf-8");
        resp.getWriter().write(WebUtil.write(json));
    }
}


5.4.2 博客列表页面功能设计

前端设计


客户端展示页面时,就需要展示文章列表数据,加载完页面就发生ajax请求,来获取文章列表内容,待收到返回的响应执行回调时,解析响应的文章列表数据


发送的ajax请求,method为get,url为blog_list,对应的后端Servlet路径也要与之对应

因为是get请求,所以没有body

设置回调函数,与前面登陆的回调函数逻辑相似,将后端返回的json字符串转化为json对象时,如果ok为true,则解析返回的数据

将返回的nickname和文章数设置到前端,将返回的文章集合以循环的方式设置到前端

<script src="js/util.js"></script>
<script>
    //客户端展示页面时,就需要展示文章列表数据
    //加载完页面,就发送ajax请求获取文章列表的数据
    //返回响应执行回调时,解析响应的文章列表数据
    ajax({
        method: "get",
        url: "blog_list",
        callback: function(status,responseText){
            if(status == 200){
                let json = JSON.parse(responseText);
                if(json.ok){
                    let data = json.data;
                    let nickname = data.nickname;
                    let h3 = document.querySelector(".card>h3");
                    h3.innerHTML = nickname;
                    let articles = data.articles;
                    let div = document.querySelector(".container-right");
                    //str不赋值就是undefined,再去拼接字符串就会出错
                    let str = "";
                    for(let a of articles){
                        //``里面可以包括单引号和双引号
                        str += `<div class="row">`;
                        str += `<div class="title">`;
                        str += a.title;
                        str += `</div>`;
                        str += `<div class="date">`;
                        str += a.dateString;
                        str += `</div>`;
                        str += `<div class="desc">`;
                        str += `<p>`;
                        str += a.content;
                        str += `</p>`;
                        str += `</div>`;
                        str += `<div class="to-detail">`;
                        str += `<a href="blog_content.html?id=` + a.id;
                        str += `">显示全文>></a>`;
                        str += `</div>`;
                        str += `</div>`;
                    }
                    div.innerHTML = str;
                    let num = data.count;
                    let count = document.querySelector("#count");
                    count.innerHTML = num;
                }else {
                    alert("ok==false");
                }
            }else {
                alert("响应状态码:"+status+"/nbody:"+responseText)
            }
        }
    });
</script>
</html>


后端Servlet设计


先校验用户是否登陆,未登录不允许访问,直接跳转到用户登陆页面,登陆后才可执行后边逻辑

登陆成功后用用户的id查询该用户的所有文章并且查询该用户的所有文章数目

此时已经登陆成功,设置JsonResult对象json的ok为true

创建一个Map结构的data,保存要返回给前端的数据文章列表和用户昵称和文章数目

将data设置到json对象中,将json对象序列化为json字符串返回给前端

@WebServlet("/blog_list")//博客列表
public class BlogListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //未登录,访问跳转到登陆页面
        User user = WebUtil.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return; //未登录,直接跳转,不会执行后边逻辑
        }
        //通过登陆用户id查找所有文章
        List<Article> articles = ArticleDao.selectById(user.getId());
        //通过登陆用户id查找文章数目
        int count = ArticleDao.getCount(user.getId());
        //先构造响应正文需要的java对象,再转化为json字符串,再设置到响应正文
        JsonResult json = new JsonResult();
        json.setOk(true);
        //前端需要的数据有nickname,articles,可以用map保存,然后设置到json.data中
        Map<String,Object> data = new HashMap<>();
        data.put("nickname",user.getNickname());
        data.put("articles",articles);
        data.put("count",count);
        json.setData(data);
        resp.setContentType("application/json; charset=utf-8");
        resp.getWriter().write(WebUtil.write(json));
    }
}
                    let article = data.article;

5.4.3 博客详情页面功能设计

前端设计 
博客详情是从博客列表的显示全文按钮跳转过来的,所以跳转的连接携带id,id标识文章id,表示显示的是哪篇文章的全部内容
先使用window.location.search.substring获取到文章id
再发送ajax请求,请求方法为get,请求url为blog_content?id=id,设置回调函数
待后端返回响应后执行回调函数,将响应正文转化为json对象,解析json对象
设置用户昵称,文章数目,文章标题,发表日期,文章内容 
<script src="js/util.js"></script>
<script>
    //window.location.search获取的是queryString?后的部分,blog_content?id=1
    //也就是获取的是?id=1
    let id = window.location.search.substring(4);
    //页面一加载就需要展示博客详情页面,所以就发送ajax请求来获取内容
    ajax({
     method: "get",
        url: "blog_content?id="+id,
        callback: function(status,responseText){
            if(status == 200){
                let json = JSON.parse(responseText);
                if(json.ok){
                    let data = json.data;
                    let nickname = data.nickname;
                    let h3 = document.querySelector(".card>h3");
                    h3.innerHTML = nickname;
                    let num = data.count;
                    let count = document.querySelector("#count");
                    count.innerHTML = num;let div = document.querySelector(".detail");
                    let str = "";
                    str += `<div class="title">`;
                    str += article.title;
                    str += `</div>`;
                    str += `<div class="date">`;
                    str += article.dateString;
                    str += `</div>`;
                    str += `<div id="article-content" class="desc">`;
                    //str += article.content;
                    str += `</div>`;
                    div.innerHTML = str;
                    //不能直接展示markdown源码,数据库保存的是markdown源码
                    editormd.markdownToHTML("article-content",{markdown: article.content});
                }else {
                    alert("ok == false");
                }
            }else {
                alert("响应状态码:"+status+"/nbody:"+responseText);
            }
        }
    });
</script>

后端Servlet设计


用户未登录不允许访问,直接跳转到用户登陆页面

请求数据携带在queryString中,所以使用req.getParameter解析请求获取到文章id

根据文章id查询整个文章将查询的数据设置到一个文章对象中

将JsonResult对象json的ok设置为true

使用一个Map结构data保存获取的文章数目,文章,用户昵称

将data设置到json对象中,将json对象序列化为json字符串后返回给前端

@WebServlet("/blog_content")//博客详情
public class BlogContentServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //未登录,访问跳转到登陆页面
        User user = WebUtil.checkLogin(req);
        if(user == null){
            resp.sendRedirect("login.html");
            return; //未登录,直接跳转,不会执行后边逻辑
        }
        //路径为:blog_content?id=文章id
        //解析请求
        String sid = req.getParameter("id"); //获取到文章id
        //通过文章id查询整篇文章
        Article a = ArticleDao.queryById(Integer.parseInt(sid));
        int count = ArticleDao.getCount(user.getId());
        JsonResult json = new JsonResult();
        json.setOk(true);
        Map<String,Object> data = new HashMap<>();
        data.put("nickname",user.getNickname());
        data.put("article",a);
        data.put("count",count);
        json.setData(data);
        resp.setContentType("application/json; charset=utf-8");
        resp.getWriter().write(WebUtil.write(json));
    }
}


5.4.4 博客编辑页面功能设计

前端设计


给发布文章绑定点击事件,点击发布,发送ajax请求用于给文章表中添加数据

获取到输入的文章标题和文章内容,将其设置为json对象

发送ajax请求的方法为post,url为blog_add,后端Servlet路径要与之对应,设置contentType为application/json,将设置的json对象转化为json字符串设置到body中

待收到后端返回的响应后,执行回调,提示发布文章成功并且跳转到博客列表页面

<script src="js/util.js"></script>
<script src="js/jquery.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.min.js"></script>
<script>
    $(function(){
        var editor = editormd("edit-content",{
            width: "100%",
            height: "calc(100% - 50px)",
            markdown: "# 在这里写下第一篇博客",
            path: "editor.md/lib/",
            saveHTMLToTextarea: true
        });
    })
    //发布文章点击事件
    function addContent(){
        let title = document.querySelector("#title").value;
        let content = document.querySelector("#content").value;
        ajax({
            method: "post",
            url: "blog_add",
            contentType: "application/json",
            body: JSON.stringify({
                title: title,
                content: content
            }),
            callback: function(status,responseText){
                if(status == 200){
                    let json = JSON.parse(responseText);
                    if(json.ok){
                        alert("发布文章成功");
                        window.location.href = "blog_list.html";
                    }else {
                        alert("ok == false");
                    }
                }else {
                    alert("响应状态码:"+status+"/nbody:"+responseText);
                }
            }
        });
    }
</script>


后端Servlet设计


用户未登录不允许访问,直接跳转到用户登录页面

使用InputStream解析请求,通过输入流获取数据

将输入流中的json字符串转化为文章对象

设置用户id,发布日期到该文章对象中

将JsonResult对象json的ok设置为true,将json对象序列化为json字符串后返回给前端

@WebServlet("/blog_add")//添加文章
public class BlogAddServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        User user = WebUtil.checkLogin(req);
        if(user == null){
            //未登录不允许访问
            resp.sendRedirect("login.html");
            return;
        }
        //解析请求
        req.setCharacterEncoding("utf-8");
        InputStream is = req.getInputStream();
        Article beInsert = WebUtil.read(is,Article.class);
        //数据库插入一条数据,相当于插入一个对象
        beInsert.setUserId(user.getId());
        beInsert.setDate(new java.util.Date());
        int n = ArticleDao.insertOne(beInsert);
        JsonResult json = new JsonResult();
        json.setOk(true);
        resp.setContentType("application/json; charset=utf-8");
        resp.getWriter().write(WebUtil.write(json));
    }
}


5.4.5 用户注销功能设计

获取到session

如果session不为空,将session中保存的user删除

删除后跳转到用户登陆页面

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //注销就是通过session对象删除保存的用户信息
        HttpSession session = req.getSession(false);
        if(session != null){
            session.removeAttribute("user");
        }
        resp.sendRedirect("login.html");
    }
}


6. 数据库逻辑处理

前端后端做业务处理的数据库逻辑操作如下:


6.1 用户表的逻辑处理

通过登陆输入的用户名和密码查询到user并将user返回,以此来做用户密码校验功能

public class UserDao {
    public static User isLogin(String username, String password){
        Connection c = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            c = DBUtil.getConnection();
            String sql = "select * from user where username=? and password=?";
            ps = c.prepareStatement(sql);
            ps.setString(1,username);
            ps.setString(2,password);
            rs = ps.executeQuery();
            User user = null;
            while(rs.next()){
                user = new User();
                int id = rs.getInt("id");
                String nickname = rs.getString("nickname");
                user.setId(id);
                user.setNickname(nickname);
                user.setUsername(username);
                user.setPassword(password);
            }
            return user;
        } catch (SQLException e) {
            throw new RuntimeException("校验账号密码出错",e);
        } finally {
            DBUtil.close(c,ps,rs);
        }
    }
    @Test
    public void testLogin(){
        System.out.println(isLogin("abc","123"));
    }
    }



6.2 文章表的逻辑处理

通过用户id查询所有文章

//通过用户id查询所有文章
    public static List<Article> selectById(Integer id){
        List<Article> articles = new ArrayList<>();
        Connection c = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            c = DBUtil.getConnection();
            String sql = "select * from article where user_id=?";
            ps = c.prepareStatement(sql);
            ps.setInt(1,id);
            rs = ps.executeQuery();
            while(rs.next()){
                Article a = new Article();
                a.setId(rs.getInt("id"));
                a.setTitle(rs.getString("title"));
                java.sql.Date date = rs.getDate("date");
                long time = date.getTime();
                a.setDate(new java.util.Date(time));
                DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                String dateString = df.format(a.getDate());
                a.setDateString(dateString);
                String content = rs.getString("content");
                a.setContent(content.length()>50 ? content.substring(0,50) : content);
                a.setUserId(id);
                articles.add(a);
            }
            return articles;
        } catch (SQLException e) {
            throw new RuntimeException("查询文章出错",e);
        } finally {
            DBUtil.close(c,ps,rs);
        }
    }
根据文章id查询整篇文章
    //根据文章id查文章
    public static Article queryById(int id) {
        Article a = null;
        Connection c = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            c = DBUtil.getConnection();
            String sql = "select * from article where id=?";
            ps = c.prepareStatement(sql);
            ps.setInt(1,id);
            rs = ps.executeQuery();
            while(rs.next()){
                a = new Article();
                a.setId(id);
                a.setTitle(rs.getString("title"));
                java.sql.Date date = rs.getDate("date");
                DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                String dateString = df.format(new java.util.Date(date.getTime()));
                a.setDateString(dateString);
                a.setContent(rs.getString("content"));
                a.setUserId(rs.getInt("user_id"));
            }
            return a;
        } catch (SQLException throwables) {
            throw new RuntimeException("查询文章详情jdbc出错",throwables);
        } finally{
            DBUtil.close(c,ps,rs);
        }
    }

 


插入一篇文章

//插入文章
    public static int insertOne(Article a) {
        Connection c = null;
        PreparedStatement ps = null;
        try{
            c = DBUtil.getConnection();
            String sql = "insert into article(title,`date`,content,user_id) values(?,?,?,?)";
            ps = c.prepareStatement(sql);
            ps.setString(1,a.getTitle());
            //ps.setDate(2,new java.sql.Date(a.getDate().getTime()))
            ps.setDate(2,new java.sql.Date(System.currentTimeMillis()));
            ps.setString(3,a.getContent());
            ps.setInt(4,a.getUserId());
            return ps.executeUpdate();
        } catch (SQLException throwables) {
            throw new RuntimeException("发布文章jdbc出错",throwables);
        } finally {
            DBUtil.close(c,ps);
        }
    }

 

获取文章数目

public static int getCount(Integer id) {
        Connection c = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            c = DBUtil.getConnection();
            String sql = "select 0 from article where user_id=?";
            ps = c.prepareStatement(sql);
            ps.setInt(1,id);
            rs = ps.executeQuery();
            int count = 0;
            while(rs.next()){
                count++;
            }
            return count;
        } catch (SQLException throwables) {
            throw new RuntimeException("查询文章数目出错",throwables);
        } finally{
            DBUtil.close(c,ps,rs);
        }
    }

 

7. 博客系统设计源码

在做前后端逻辑处理的时候,前端代码有些稍微的改动,本文没有提及到,请点击查看源码,查看改动的细节以及所有后端的设计实现:个人博客系统设计源码


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1天前
|
运维 自然语言处理 供应链
Java云HIS医院管理系统源码 病案管理、医保业务、门诊、住院、电子病历编辑器
通过门诊的申请,或者直接住院登记,通过”护士工作站“分配患者,完成后,进入医生患者列表,医生对应开具”长期医嘱“和”临时医嘱“,并在电子病历中,记录病情。病人出院时,停止长期医嘱,开具出院医嘱。进入出院审核,审核医嘱与住院通过后,病人结清缴费,完成出院。
13 3
|
5天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
10 1
|
6天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
10天前
|
移动开发 前端开发 JavaScript
java家政系统成品源码的关键特点和技术应用
家政系统成品源码是已开发完成的家政服务管理软件,支持用户注册、登录、管理个人资料,家政人员信息管理,服务项目分类,订单与预约管理,支付集成,评价与反馈,地图定位等功能。适用于各种规模的家政服务公司,采用uniapp、SpringBoot、MySQL等技术栈,确保高效管理和优质用户体验。
|
12天前
|
XML JSON 监控
告别简陋:Java日志系统的最佳实践
【10月更文挑战第19天】 在Java开发中,`System.out.println()` 是最基本的输出方法,但它在实际项目中往往被认为是不专业和不足够的。本文将探讨为什么在现代Java应用中应该避免使用 `System.out.println()`,并介绍几种更先进的日志解决方案。
34 1
|
16天前
|
Java 关系型数据库 API
介绍一款Java开发的企业接口管理系统和开放平台
YesApi接口管理平台Java版,基于Spring Boot、Vue.js等技术,提供API接口的快速研发、管理、开放及收费等功能,支持多数据库、Docker部署,适用于企业级PaaS和SaaS平台的二次开发与搭建。
|
20天前
|
Java Apache Maven
Java/Spring项目的包开头为什么是com?
本文介绍了 Maven 项目的初始结构,并详细解释了 Java 包命名惯例中的域名反转规则。通过域名反转(如 `com.example`),可以确保包名的唯一性,避免命名冲突,提高代码的可读性和逻辑分层。文章还讨论了域名反转的好处,包括避免命名冲突、全球唯一性、提高代码可读性和逻辑分层。最后,作者提出了一个关于包名的问题,引发读者思考。
Java/Spring项目的包开头为什么是com?
|
20天前
|
前端开发 Java 数据库连接
基于Java的校车管理系统(下)
基于Java的校车管理系统(下)
13 0
|
9天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
80 38
|
6天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?