超详细!适合大学生的第一个项目:博客系统

1.项目简介

本项目基于java实现。项目通过前后端分离技术,基于Servlet开发而成,通过maven打包至Tomcat进行部署,再通过浏览器把前端页面渲染出来。

本项目使用了MySql数据库来存储数据。

前端使用了四个页面:登录页,博客列表页,博客编辑页,博客详细页。结合后端代码实现了:博客登录,博客列表,博客编辑,博客发布,注销用户等功能。

使用软件:IDEA、MySql、VS Code、edge浏览器。

目录

1.项目简介

2.项目演示

2.1 登录

2.2 博客列表

2.3 博客详情​编辑

2.4 博客编辑页

3.整体项目结构​编辑

4.数据库准备

4.1 关系建立

4.2 sql编写

4.3 封装数据库

5.Servlet项目创建

6.完善model

6.1 完善Blog

6.2 完善User

7.博客列表

7.1 约定前后端交互接口

7.2 后端代码

7.3 DAO类方法

7.4 前端代码

7.5 修改细节

8.博客详情

8.1 约定前后端交互接口

8.2 后端代码

8.3 DAO类方法

8.4 前端代码

9.博客登录

9.1 约定前后端交互接口

9.2 后端代码

9.3 DAO类方法

9.4 前端代码

10.检测登录

10.1 约定前后端交互接口

10.2 后端代码

10.3 前端代码

11.显示用户信息

11.1 约定前后端交互接口

11.2 后端代码

11.3 前端代码

12.用户注销

12.1 前后端交互接口

12.2 后端代码

12.3 前端代码

13.编辑博客(发布博客)

13.1 约定前后端交互接口

13.2 后端代码

13.3 DAO方法

13.4 前端代码


2.项目演示

2.1 登录

2.2 博客列表

2.3 博客详情

2.4 博客编辑页

3.整体项目结构超详细!适合大学生的第一个项目:博客系统_第1张图片

4.数据库准备

4.1 关系建立

我们需要准备两张表,一张是用户表。用户输入了正确的用户名和密码才能够登录。并且把用户名作为博客的作者名字。另一张是博客表,存储博客的标题、正文、时间、博客Id、作者Id。

User : userId  username  password
Blog : blogId  title     content    postTime  userId

User表中,userId作为自增主键。Blod表中,blogId作为自增主键。同时Blog中的userId作为外键和User中的userId存在关联。

4.2 sql编写

我们直接在MySQL中编写sql语句容易出错,可以先把sql语句放到记事本或者IDEA中,方便我们后续检查和匹配代码。

create database if not exists blog_system;

drop table if exists Blog;
drop table if exists User;

create table Blog(
    blogId int primary key auto_increment,
    title varchar(100),
    content varchar(4096),
    postTime datetime,
    userId int
);

create table User(
    userId int primary key auto_increment,
    username varchar(255) unique,
    password varchar(255)
);

前几句语句中,通过if not exists和if exists 来验证了数据库、表的唯一和准确性。

4.3 封装数据库

我们通过一个单独的文件来把数据库的基本操作封装好,例如连接数据库等。之后只需要调用这里面的方法就可以完成连接。

package DAO;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.*;

public class DBUtil {
    private static DataSource dataSource = new MysqlDataSource();

    static{
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/BlogSystem?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("123456");
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
        if(connection != null){
            try{
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(preparedStatement != null){
            try{
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(resultSet != null){
            try{
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

5.Servlet项目创建

具体操作可以看博主前面的文章:从零到servlet第一个helloicon-default.png?t=N7T8http://t.csdnimg.cn/Lkkk5

同时我们可以把我们需要的包一次性引入到pom.xml.

servlet 3.1.0  mysql 5.1.49  jackson 2.14.2

    
        
        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        

        
        
            mysql
            mysql-connector-java
            5.1.49
        

        
        
            com.fasterxml.jackson.core
            jackson-databind
            2.14.2
        

    

这样我们就可以正式开始项目的编写了!虽然本项目采用前后端分离技术,但主要讲解的还是后端部分,前端代码已经完成,本文就不再叙述前端代码。

6.完善model

在这里面,我们需要定义相关的成员变量和构造方法。其中变量名和数据库中的名字要符合一致。

6.1 完善Blog

package model;

import java.sql.Timestamp;

public class Blog {
    private int blogId;
    private String title;
    private String content;
    private Timestamp postTime;
    private  int userId;

    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 Timestamp getPostTime() {
        return postTime;
    }

    public void setPostTime(Timestamp postTime) {
        this.postTime = postTime;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
}

6.2 完善User

package model;

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

7.博客列表

7.1 约定前后端交互接口

当我们想要访问博客列表时,前端给后端发起请求。同时后端获取到所有的博客返回给前端。

blog.list页面发起的请求:超详细!适合大学生的第一个项目:博客系统_第2张图片

 在API中创建BlogServlet类,用来处理GET请求,并且返回响应:

超详细!适合大学生的第一个项目:博客系统_第3张图片

7.2 后端代码

为了能够后端传输的时候传多篇博客,所以我们用一个数组把所有的博客放进去。通过ObjectMapper类,它可以帮助我们快速的进行各个类型和Json类型的相互转换。在这里是把Blog这个数组转成字符串传到前端。

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List blogs = BlogDAO.selectAll();
        String respJson = objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

7.3 DAO类方法

通过BlogDAO.selectAll()方法拿到的所有的blog数据,那么我们需要在DAO类中完善相关的数据库查询代码。

package DAO;

import model.Blog;

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 {
    public List selectAll(){
        List blogs = new ArrayList<>();

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select * from Blog";
            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.setPostTime(resultSet.getTimestamp("postTime"));
                blog.setUserId(resultSet.getInt("userId"));
                blogs.add(blog);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return blogs;
    }
}

值得注意的是,content部分如果超过了一个特定的值,可以让其只显示出前一部分,以便更好的展示在列表页中。

7.4 前端代码

因为前端并不是我们的重点,所以我们的目光只是放在如何与后端交互上。超详细!适合大学生的第一个项目:博客系统_第4张图片

通过ajax发送get请求,并且完善接收的代码就能让博客的相关信息显示出来。

7.5 修改细节

超详细!适合大学生的第一个项目:博客系统_第5张图片

在后端代码中,我们用了setpostTime来获取到时间,但是这样获取到的却是时间戳类型的数据。我们需要修改获取到的时间类型。在Blog类中增加一个方法,来让时间戳变成易于读取的类型。

    public String getTime(){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return simpleDateFormat.format(postTime);
    }

 完善后效果:

超详细!适合大学生的第一个项目:博客系统_第6张图片

同时我们要规定一个查询的顺序,把新的博客放在上面,老的博客放在下面。

超详细!适合大学生的第一个项目:博客系统_第7张图片

8.博客详情

8.1 约定前后端交互接口

假定要获取blogId为1的博客,先约定前后端交互接口。超详细!适合大学生的第一个项目:博客系统_第8张图片

8.2 后端代码

在博客列表中,发送的请求是:GET /blog,那么博客详情页只是多了一个后面的blogId=1的参数,那么就可以把相关的servlet写到Blog_Servlet中,用blogId作为区分。

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        BlogDAO blogDAO = new BlogDAO();
        String blogId = req.getParameter("blogId");
        if(blogId == null){
            List blogs = blogDAO.selectAll();
            String respJson = objectMapper.writeValueAsString(blogs);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
            //if里面是博客列表页的servlet
        }else{
            Blog blog = blogDAO.selectById(blogId);
            String respJson = objectMapper.writeValueAsString(blog);
            resp.setContentType("application/json;charset=utf8");
            resp.getWriter().write(respJson);
            //else里面是博客详情页的servlet
        }
    }
}

8.3 DAO类方法

通过selectById这个方法来拿到指定博客的全部信息。

    public Blog selectById(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);
            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.setPostTime(resultSet.getTimestamp("postTime"));
                blog.setUserId(resultSet.getInt("userId"));
                return blog;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally{
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

8.4 前端代码

超详细!适合大学生的第一个项目:博客系统_第9张图片

9.博客登录

此处的登录并没有注册功能辅助,所以需要我们提前在数据库中添加用户。然后再在登录页中登录。

insert into user values(1,'张三','123');
insert into user values(2,'李四','123');

9.1 约定前后端交互接口

超详细!适合大学生的第一个项目:博客系统_第10张图片

前端获取到username和password后,通过json字符串的方式传给后端。后端接收后判定用户名和密码是否正确。正确就跳转到博客列表页。

9.2 后端代码

登录的Servlet需要在LoginServlet中完成。

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf8");
        resp.setContentType("text/html;charset=utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null || "".equals(username) || password == null || "".equals(password)){
            String html = "缺少用户名或者密码,请检查";
            resp.getWriter().write(html);
            return;
        }
        UserDAO userDAO = new UserDAO();
        User user = userDAO.selectByUsername(username);
        if(user == null){
            String html = "

登录失败! 用户名或密码错误

"; resp.getWriter().write(html); return; } if(!password.equals(user.getPassword())){ String html = "

登录失败! 用户名或密码错误

"; resp.getWriter().write(html); return; } HttpSession session = req.getSession(true); session.setAttribute("user",user); resp.sendRedirect("blog_list.html"); } }

9.3 DAO类方法

    public User selectByUsername(String name) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try{
            DBUtil.getConnection();
            String sql = "select * from User where username = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,name);
            resultSet = 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 {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

在UserDAO中,完善了selectByUsername,按照用户的名字来查询用户的相关信息(如密码)。和前端发来的数据相匹配。最后一致则创建一个session。

9.4 前端代码

超详细!适合大学生的第一个项目:博客系统_第11张图片

 通过一个form表单来把数据提交给后端。

10.检测登录

在某些情况,用户如果直接输入  http://localhost:8080/Blog-System/blog_list.html 这个页面,可能会出现跳过登录的环节。毕竟登录的代码最后直接重定向到了博客列表的页面,所以我们就需要防止用户直接在地址栏输入正确的页面直接跳转也能浏览。

具体怎么做呢?我们的做法是在博客列表页、详情页、编辑页中,在页面加载后,发起一个ajax;这个ajax从服务器获取当前登陆状态,若当下是未登录,就直接重定向到登录页,若已登录,不做任何处理。

所以我们在每一个html中都需要加入这段ajax,后端可以共用LoginServlet,用一个GET方法来返回结果。

10.1 约定前后端交互接口

超详细!适合大学生的第一个项目:博客系统_第12张图片

10.2 后端代码

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json;charset=utf8");
        HttpSession session = req.getSession(false);
        if(session == null){
            User user = new User();
            String respJson = objectMapper.writeValueAsString(user);
            resp.getWriter().write(respJson);
            return;
        }
        User user = (User) session.getAttribute("user");
        if(user == null){
            user = new User();
            String respJson = objectMapper.writeValueAsString(user);
            resp.getWriter().write(respJson);
            return;
        }
        String respJson = objectMapper.writeValueAsString(user);
        resp.getWriter().write(respJson);
    }

首先通过getSession拿到当前页面的session。如果当前页面有session和user,才说明用户是正常登录上去的。没有session说明用户直接在地址栏输入的该页面。如果有session但是没有用户,说明用户已经注销了但是session并没有消除。所以这个地方需要判定两次,一次是session,一次是user。返回的是空用户。

10.3 前端代码

前端可以写一个function函数,在除去login的页面中都填加上。超详细!适合大学生的第一个项目:博客系统_第13张图片

11.显示用户信息

具体来说,就是在博客列表页展示当前登录用户的用户名,在博客详情页展示当前博客作者的用户名。

超详细!适合大学生的第一个项目:博客系统_第14张图片

11.1 约定前后端交互接口

超详细!适合大学生的第一个项目:博客系统_第15张图片

也就是说,只需要在这两个页面中的前端代码中,获取到username,显示出来就可以了。

11.2 后端代码

博客列表页:这里的代码还是刚刚的检测登录页的代码。返回给前端的是user用户,所以前端再拿到这个用户名就可以了。

博客详情页:这里需要完成AuthorServlet,来额外获取到文章的作者。

@WebServlet("/author")
public class AuthorServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String blogId = req.getParameter("blogId");
        if(blogId == null){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("参数非法,缺少blogId");
            return;
        }
        BlogDAO blogDAO = new BlogDAO();
        Blog blog = blogDAO.selectById(Integer.parseInt(blogId));
        if(blog == null){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("参数非法,缺少blogId");
            return;
        }
        UserDAO userDAO = new UserDAO();
        User author = userDAO.selectByUserId(blog.getUserId());
        String respJson = objectMapper.writeValueAsString(author);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

11.3 前端代码

博客列表页:

超详细!适合大学生的第一个项目:博客系统_第16张图片

博客详情页:

超详细!适合大学生的第一个项目:博客系统_第17张图片

12.用户注销

注销功能是通过a标签来完成的。具体方式是服务器删除user对象。前面提到,只有session和user同时存在才能够登陆成功。那么只要有任何一个条件不存在就代表着用户注销了。因为session没有一个合适的删除方法(只有等待过期),所以我们采用删除user对象的方式。

12.1 前后端交互接口

超详细!适合大学生的第一个项目:博客系统_第18张图片

因为没有logout接口,我们需要新建一个LogoutServlet。

12.2 后端代码

@WebServlet
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if(session == null){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前未登录");
            return;
        }
        session.removeAttribute("user");
        resp.sendRedirect("login.html");
    }
}

其实很简单,关键只用了一个removeAttribute来把user移出。这样判定到登录状态就会是未登录。

12.3 前端代码

只需要在每一个页面的a标签上改成logout这个接口就可以完成注销操作。

超详细!适合大学生的第一个项目:博客系统_第19张图片

13.编辑博客(发布博客)

13.1 约定前后端交互接口

可以直接复用BlogServlet中的blog接口。在此之前我们在这里面用doGet实现了博客列表页和博客详情页的查询,现在我们可以用doPost方法来实现博客发布。超详细!适合大学生的第一个项目:博客系统_第20张图片

13.2 后端代码

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if(session == null){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前未登录,请退出登录");
            return;
        }
        User user = (User) session.getAttribute("user");
        if(user == null){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前未登录,请退出登录");
            return;
        }
        req.setCharacterEncoding("utf8");
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if((title == null) || ("".equals(title)) || (content == null) || ("".equals(content))){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前提交数据有误,请保证不为空再提交");
            return;
        }
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setPostTime(new Timestamp(System.currentTimeMillis()));
        blog.setUserId(user.getUserId());
        BlogDAO blogDAO = new BlogDAO();
        blogDAO.add(blog);
        resp.sendRedirect("blog_list.html");
    }

通过blogDAO中的add方法,把新的blog添加到数据库中。

13.3 DAO方法

add方法相对来说已经比较简单了。但因为是新增数据,所以没有查询的结果,也就不需要executeQuery,而是executeUpdate。并且不需要resultSet。

    public void add(Blog blog){
        Connection connection = null;
        PreparedStatement statement = null;
        try{
            connection = DBUtil.getConnection();
            String sql = "insert into Blog values(null,?,?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setTimestamp(3,blog.getPostTime());
            statement.setInt(4,blog.getUserId());
            statement.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,null);
        }
    }

13.4 前端代码

只需要在原有的基础上加上form表单就可以把数据到后端。超详细!适合大学生的第一个项目:博客系统_第21张图片

 

至此,博客系统的基本样式已经完成。可以根据博主的内容进行深度拓展~

你可能感兴趣的:(servlet,mysql,tomcat,maven,html)