Servlet综合练习:个人博客系统

功能简介

1)注册新用户
Servlet综合练习:个人博客系统_第1张图片
Servlet综合练习:个人博客系统_第2张图片
2)登录已有用户
Servlet综合练习:个人博客系统_第3张图片
Servlet综合练习:个人博客系统_第4张图片
3)展示博客列表(包含文章标题以及作者信息),点击标题就会跳转到文章详情页
Servlet综合练习:个人博客系统_第5张图片
4)文章详情页中,显示文章标题,作者,以及文章内容
Servlet综合练习:个人博客系统_第6张图片
5)发布新的博客
Servlet综合练习:个人博客系统_第7张图片
Servlet综合练习:个人博客系统_第8张图片
6)删除自己的博客
点击文章列表后边的删除按钮就会删除文章,注意每个用户只能删除自己写的文章。
Servlet综合练习:个人博客系统_第9张图片

整个设计的目录结构

Servlet综合练习:个人博客系统_第10张图片

使用技术

maven:使用maven来管理依赖,打包项目
mysql:使用MySQL作为业务数据的存储
html:使用HTML来编写前端页面
tomcat:使用Tomcat作为Web项目部署的服务器
servlet:每个页面调用后台接口都需要使用Servlet来完成业务。
多线程:synchronized、volatile关键字,在多线程下访问Servlet共享变量,需要保证线程安全

项目准备

需要的资源

Maven、IDEA、MySQL、Chrome浏览器、Fiddler4抓包工具

创建项目

Servlet综合练习:个人博客系统_第11张图片
加入依赖:a)Servlet API b)MySQL connector
配置pom.xml 文件:
Servlet综合练习:个人博客系统_第12张图片

数据库设计

设计当前的代码中用到的数据库中有几张表,每个表包含哪些字段,每个表之间的关联关系。在这里我们需要两张表,用户和文章表。

  • 创建数据库
  • 设计用户表
    需要userId、name、password字段
  • 设计文章表
    需要articleId、title、content字段,且需要关联用户表,关系表现为1个用户多篇文章,所以在文章表建立外键userId关联用户表的主键userId

db.sql:

drop database if exists java16_blogdemo;
create database java16_blogdemo;

use java16_blogdemo;

drop table if exists user;
create table user (
    userId int primary key auto_increment,
    name varchar(50) unique,
    password varchar(50)
);

drop table if exists article;
create table article (
    articleId int primary key auto_increment,
    title varchar(255),
    content text,
    userId int,
    foreign key(userId) references user(userId)
);

Servlet综合练习:个人博客系统_第13张图片

实现数据库相关操作

创建一个DBUtil类来管理链接

package modle;

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;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName DBUtil
 * @Description
 * @Author by小房
 * @Date 2020/7/15 17:51
 */
// 管理数据库连接   建立连接    断开连接
public class DBUtil {
    private static volatile DataSource dataSource = null;
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/java16_blogdemo?characterEncoding=utf-8&useSSL=true";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "***";//这里写自己的密码

    public static DataSource getDataSource() {
        if (dataSource == null) {
            synchronized (DBUtil.class) {
                if (dataSource == null) {
                    dataSource = new MysqlDataSource();
                    ((MysqlDataSource)dataSource).setURL(URL);
                    ((MysqlDataSource)dataSource).setUser(USERNAME);
                    ((MysqlDataSource)dataSource).setPassword(PASSWORD);

                }
            }
        }
        return dataSource;
    }
    // 通过这个方法来建立连接

     public static Connection getConnection() {
         try {
             return getDataSource().getConnection();
         } catch (SQLException e) {
             e.printStackTrace();
         }
         return null;
     }
    // 通过这个方法来断开连接
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {

            try {
                if (resultSet != null) {
                resultSet.close();
                }

                if (statement != null) {
                    statement.close();
                }

                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
    }

}

创建实体类User Article

User:

package modle;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName User
 * @Description
 * @Author by小房
 * @Date 2020/7/15 17:50
 */
public class User {
    private int userId;
    private String name;
    private String password;

    public int getUserId() {
        return userId;
    }

    public String getName() {
        return name;
    }

    public String getPassword() {
        return password;
    }

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

    public void setName(String name) {
        this.name = name;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

article:

package modle;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName Article
 * @Description
 * @Author by小房
 * @Date 2020/7/15 17:51
 */
public class Article {
    private int articleId;
    private String title;
    private String content;
    private int userId;

    public int getArticleId() {
        return articleId;
    }

    public String getTitle() {
        return title;
    }

    public String getContent() {
        return content;
    }

    public int getUserId() {
        return userId;
    }

    public void setArticleId(int articleId) {
        this.articleId = articleId;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setContent(String content) {
        this.content = content;
    }

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

    @Override
    public String toString() {
        return "Article{" +
                "articleId=" + articleId +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", userId=" + userId +
                '}';
    }
}

实现数据库的基本增删改查

Dao表示数据访问层
通过UserDao 这个类来实现对用户数据库表的操作

package modle;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName UserDao
 * @Description
 * @Author by小房
 * @Date 2020/7/16 22:03
 */
public class UserDao {
    // 1、新增用户
      public void add(User user) {
        // 1、获取数据库连接
        Connection connection = DBUtil.getConnection();
        // 2、拼装 sql
        String sql = "insert into  user values (null, ?, ?)";
        PreparedStatement statement = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1, user.getName());
            statement.setString(2, user.getPassword());
            // 3、执行 sql
            int ret = statement.executeUpdate();
            if (ret != 1) {
                System.out.println("插入新用户失败");
                return;
            }
            System.out.println("插入新用户成功");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4、释放数据库连接
            DBUtil.close(connection, statement, null);
        }
    }

    // 2、按照名字查找用户

    public User selectByName(String name) {
        // 1、建立数据库连接
        Connection connection = DBUtil.getConnection();
        // 2、拼装sql
        String sql = "select * from user where name = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1, name);
            // 3、执行 sql
            resultSet = statement.executeQuery();
            // 4、遍历结构集
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setName(resultSet.getString("name"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }

    // 根据用户 id 找到用户信息
    public User selectById(int userId) {
        // 1、建立数据库连接
        Connection connection = DBUtil.getConnection();
        // 2、拼装sql
        String sql = "select * from user where userId = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, userId);
            // 3、执行 sql
            resultSet = statement.executeQuery();
            // 4、遍历结构集
            if (resultSet.next()) {
                User user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setName(resultSet.getString("name"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }
    /*public static void main(String[] args) {
        UserDao userDao = new UserDao();
        // 1、先测试 add 方法
        //User user = new User();
        //user.setName("***");
       // user.setPassword("********");
       // userDao.add(user);
        // 2、测试 selectByName
        //User user = userDao.selectByName("fwh");
        //System.out.println(user);
    }*/
}

通过ArticleDao 这个类来实现对文章的数据库操作

package modle;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName ArticleDao
 * @Description
 * @Author by小房
 * @Date 2020/7/16 20:43
 */
public class ArticleDao {
    // 1、新增文章
    public void add (Article article) {
        // 1、获取数据库连接
        Connection connection = DBUtil.getConnection();
        // 2、构造  sql
        String sql = "insert into article values (null, ?, ?, ?)";
        PreparedStatement statement = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1, article.getTitle());
            statement.setString(2, article.getContent());
            statement.setInt(3, article.getUserId());
            // 3、执行sql
            int ret = statement.executeUpdate();
            if (ret != 1) {
                System.out.println("执行插入文章操作失败");
                return;
            }
            System.out.println("执行插入文章操作成功");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4、释放连接
            DBUtil.close(connection, statement, null);
        }
    }


    // 2、查看文章列表

    public List<Article> selectAll() {
        List<Article> articles = new ArrayList<Article>();
        // 1、建立连接
        Connection connection = DBUtil.getConnection();
        // 2、拼装 sql
        String sql = "select articleId, title, userId from article";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.prepareStatement(sql);
            // 3、执行 sql
            resultSet = statement.executeQuery();
            // 4、遍历结果集
            while (resultSet.next()) {
                // 针对每一个结果记录,都构造一个对应的 Article 对象
                Article article = new Article();
                article.setArticleId(resultSet.getInt("articleId"));
                article.setTitle(resultSet.getString("title"));
                article.setUserId(resultSet.getInt("userId"));
                articles.add(article);
            }
            return articles;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }

    // 3、查看指定文章详情
    public Article selectById (int articleId) {
        // 1、建立数据库连接
        Connection connection = DBUtil.getConnection();
        // 2、构造 SQL
        String sql = "select * from article where articleId = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, articleId);
            // 3、执行 sql
            resultSet = statement.executeQuery();
            // 4、便利结果集
            if (resultSet.next()) {
                Article article = new Article();
                article.setArticleId(resultSet.getInt("articleId"));
                article.setTitle(resultSet.getString("title"));
                article.setContent(resultSet.getString("content"));
                article.setUserId(resultSet.getInt("userId"));
                return article;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return null;
    }

    //  4、删除指定文章
    public void delete  (int articleId) {
        // 1、获取连接
        Connection connection = DBUtil.getConnection();
        // 2、拼装 sql
        String sql = "delete from article where articleId = ?";
        PreparedStatement statement = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setInt(1, articleId);
            int ret = statement.executeUpdate();
            if (ret != 1) {
                System.out.println("删除文章失败");
                return;
            }
            System.out.println("删除文章成功");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, null);
        }
    }

    /*public static void main(String[] args) {
        ArticleDao articleDao = new ArticleDao();
        // 1、测试新增文章
        Article article = new Article();
        article.setTitle("this is title 2");
        article.setContent("this is content 1 this is content 1 this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1this is content 1");
        article.setUserId(1);
        articleDao.add(article);
    }*/
}

单元测试

我们可以针对上边的数据库操作进行简单的测试,有助于提前发现问题,在这里我们需要一些库/框架可以便于测试。
pom.xml 中导入我们的依赖:
Servlet综合练习:个人博客系统_第14张图片

一个常见问题

我们可能会出现数据库连接不上,就会抛出异常
常见原因:
1)url 写错了
2)用户名,密码错误
3)数据库服务器没有正常启动
4)连接了其他主机的数据库

注意:如果拼装 sql ,直接使用String 来按照字符串拼接的方式来拼装,这样会出现一些问题,比如:不方便也不安全。要尽可能防止SQL注入
SQL注入理解:
Servlet综合练习:个人博客系统_第15张图片

进行前后端接口设计

Servlet综合练习:个人博客系统_第16张图片
Servlet综合练习:个人博客系统_第17张图片

分别实现接口API

LoginServlet:

package api;

import modle.User;
import modle.UserDao;
import view.HtmlGenerator;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName LoginServlet
 * @Description
 * @Author by小房
 * @Date 2020/7/21 15:12
 */
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        //1、获取到用户名和密码, 并进行见到校验
        String name = req.getParameter("name");
        String password = req.getParameter("password");
        if (name == null || "".equals(name) || password == null || "".equals(password)) {
            String html = HtmlGenerator.getMessagePage("用户名和密码为空", "login.html");
            resp.getWriter().write(html);
            return;
        }
        // 2、数据库中查找,看用户是否存在
        // 3、对比密码是否正确
        UserDao userDao = new UserDao();
        User user = userDao.selectByName(name);
        if (user == null || !password.equals(user.getPassword())) {
            String html = HtmlGenerator.getMessagePage("用户名或者密码输入错误", "login.html");
            resp.getWriter().write(html);
            return;
        }
        // 4、匹配成功则认为登录成功,创建一个 Session
        HttpSession httpSession = req.getSession(true);
        httpSession.setAttribute("user", user);
        String html = HtmlGenerator.getMessagePage("登录成功!", "article");
        resp.getWriter().write(html);
    }
}

RegisterServlet:

package api;

import modle.User;
import modle.UserDao;
import view.HtmlGenerator;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName RegisterServlet
 * @Description
 * @Author by小房
 * @Date 2020/7/20 16:46
 */
public class RegisterServlet extends HttpServlet {
    //浏览器通过 POST 方法提交注册信息给服务器

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        // 1、获取到前端提交的数据(用户名, 密码),校验是否合法
        String name = req.getParameter("name");
        String password = req.getParameter("password");
        if (name == null || "".equals(password)) {
            // 用户提交的数据有误,返回一个错误页面
            String html = HtmlGenerator.getMessagePage("用户名或者密码输入错误", "register.html");
            resp.getWriter().write(html);
            return;
        }
        //2、拿着用户名在数据库中进行查找, 看看用户名是否已经存在,如果存在,认为注册失败(用户名不能重复)
        UserDao userDao = new UserDao();
        User existUser = userDao.selectByName(name);
        if (existUser != null) {
            String html = HtmlGenerator.getMessagePage("用户名重复,请换个名字!", "register.html");
            resp.getWriter().write(html);
            return;
        }

        //3、根据前端提交的数据, 构造 User 对象并插入到数据库当中
        User user = new User();
        user.setName(name);
        user.setPassword(password);
        userDao.add(user);

        // 4、返回一个结果页面, 提示当前注册成功
        String html = HtmlGenerator.getMessagePage("注册成功!点击跳转登录页面", "login.html");
        resp.getWriter().write(html);
    }
}

ArticleServlet:

package api;

import modle.Article;
import modle.ArticleDao;
import modle.User;
import modle.UserDao;
import view.HtmlGenerator;

import javax.servlet.ServletException;
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.util.List;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName ArticleServlet
 * @Description
 * @Author by小房
 * @Date 2020/7/21 15:34
 */
public class ArticleServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        // 1、验证用户是否已经登录,如果尚未登录,就提示用户进行登录操作
        HttpSession httpSession = req.getSession(false);
        if (httpSession == null) {
            // 当前状态就是未登录状态
            String html = HtmlGenerator.getMessagePage("请先进行登录!", "login.html");
            resp.getWriter().write(html);
            return;
        }
        User user = (User) httpSession.getAttribute("user");
        // 2、判断请求是否存在 articleId 参数
        String articleIdStr = req.getParameter("articleId");
        if (articleIdStr == null) {
            // a)没有这个参数就去执行获取文章列表操作
            getAllArticle(user, resp);
        } else {
            // b) 有这个参数就去执行获取文章具体内容
            getOneArticle(Integer.parseInt(articleIdStr), user, resp);
        }
    }

    private void getOneArticle(int articleId, User user, HttpServletResponse resp) throws IOException {
        // 1、查找数据库
        ArticleDao articleDao = new ArticleDao();
        Article article = articleDao.selectById(articleId);
        if (article == null) {
            // 文章未找到
            String html = HtmlGenerator.getMessagePage("文章不存在", "article");
            resp.getWriter().write(html);
            return;
        }
        // 2、根据作者 id 找到作者信息,进一步得到作者姓名
        UserDao userDao = new UserDao();
        User author = userDao.selectById(article.getUserId());   //在这里刚才错误  在这里是根据用户id 获得作者信息
        // 3、构造页面
        String html = HtmlGenerator.getArticleDetailPage(article, user, author);
        resp.getWriter().write(html);
    }

    private void getAllArticle(User user, HttpServletResponse resp) throws IOException {
        // 1、查找数据库
        ArticleDao articleDao = new ArticleDao();
        List<Article> articles = articleDao.selectAll();
        // 2、构造页面
        String html = HtmlGenerator.getArticleListPage(articles, user);
        resp.getWriter().write(html);
    }

    // 实现新增文章的逻辑

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html; charset=utf-8");
        // 1、判断用户登录状态,如果用户上未登录,就要提醒用户登录
        HttpSession httpSession = req.getSession(false);
        if (httpSession == null) {
            String html = HtmlGenerator.getMessagePage("您尚未登录", "login.html");
            resp.getWriter().write(html);
            return;
        }
        User user = (User) httpSession.getAttribute("user");
        // 2、从请求中读取浏览器提交的数据(title , content) ,并进行简单的校验
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if (title == null || "".equals(title)
                || content == null ||"".equals(content)) {
            String html = HtmlGenerator.getMessagePage("提交的标题或者内容为空", "article");
            resp.getWriter().write(html);
            return;
        }
        // 3、把数据放在数据库中
        ArticleDao articleDao = new ArticleDao();
        Article article = new Article();
        article.setUserId(user.getUserId());
        article.setContent(content);
        article.setTitle(title);
        articleDao.add(article);
        // 4、返回一个插入成功的页面
        String html = HtmlGenerator.getMessagePage("发布成功!", "article");
        resp.getWriter().write(html);
        return;
    }
}

DeleteArticleServlet:

package api;

import modle.Article;
import modle.ArticleDao;
import modle.User;
import view.HtmlGenerator;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @ Created with IntelliJ IDEA.
 * @ClassName DeleteArticleServlet
 * @Description
 * @Author by小房
 * @Date 2020/7/21 18:08
 */
public class DeleteArticleServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        // 1、验证用户的登录状态,如果未登录,肯定不能删除
        HttpSession httpSession = req.getSession(false);
        if (httpSession == null) {
            String html = HtmlGenerator.getMessagePage("您尚未登陆!", "login.html");
            resp.getWriter().write(html);
            return;
        }
        User user = (User) httpSession.getAttribute("user");

        // 2、根据请求内容, 获取要删除的文章id
        String articleIdStr = req.getParameter("articleId");
        if (articleIdStr == null || "".equals(articleIdStr)) {
            String html = HtmlGenerator.getMessagePage("要删除的文章不存在", "article");
            resp.getWriter().write(html);
            return;
        }

        // 3、根据文章id 查到文章的作者,只有当文章作者是当前用户才可以进行删除操作
        ArticleDao articleDao = new ArticleDao();
        Article article = articleDao.selectById(Integer.parseInt(articleIdStr));
        if (article.getUserId() != user.getUserId()) {
            String html = HtmlGenerator.getMessagePage("您只能删除自己的文章!", "article");
            resp.getWriter().write(html);
            return;
        }
        // 4、真正执行删除操作
        articleDao.delete(Integer.parseInt(articleIdStr));
        // 5、返回一个删除成功的页面
        String html = HtmlGenerator.getMessagePage("删除成功!",
                "article");
        resp.getWriter().write(html);
    }
}

注意:如果我们出现一些问题一定不要慌,要去定位问题解决问题。
比如:提交没反应
我们先用Fiddle 抓包看看,如果请求构造的有问题,那么就是网页的问题;如果请求构造ok,相应的结果错误,那么就是服务器的问题;如果请求构造ok,响应结果ok,可能还是网页的问题。

GitHub链接

java_blogDemo 完整代码

在这里插入图片描述

你可能感兴趣的:(web项目,java,web,mysql,数据库)