Server version blog system, front-end and back-end interaction 1

1. Preparation work

1). Create maven project

2). Introduce dependencies servlet, jackson, mysql

<dependencies>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.6.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
</dependencies>

3). Create necessary directories

Insert image description here

web.xml

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
</web-app>

4). Write code

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.getWriter().write("hello");
    }
}

5). 6). Package deployment (directly based on smart tomcat)

Content Path: /blog_system

7). Verify in browser

Start the server and visit: 127.0.0.1:8080/blog_system/hello

Browser page: hello


2. Copy the front-end code

Insert image description here

At present, V has been implemented almost~~ You can introduce the front-end page into the project and copy it directly to the webapp directory. Be careful not to put the wrong directory!

These files can be understood as static resources.
When packaged and deployed later, these static resources will also be packaged and deployed, and they can be accessed in the browser later~

C and M, first implement the Model layer and implement database-related code.


3. Write database operation code

1. Create database/table structure => (database design)

Design database needs to be designed according to current needs

Blog page:

  1. Blog list page: displays the list of blogs
  2. Blog details page: Click on the blog list page, the blog entries listed above will jump to the page, and the complete content of the blog will be displayed.
  3. Login page
  4. Blog editing page: Created a markdown editor based on editor.md~~ Posted a blog based on this page

Store the blog . When you click Publish, the blog is published to the server and will be saved to
obtain the blog . On the blog list page and blog details page, you can get the content of the blog and perform login verification~~

To design the table, you need to capture the entities in the requirements (key nouns)

  • Blog table, used to store all blog data
  • User table, user login needs to use this table

In the main directory, create db.sql

-- 编写建库建表的 sql

-- 创建数据库
create database if not exists java_blog;

use java_blog;

-- 创建博客表
drop table if exists blog;
create table blog (
    blogId int primary key auto_increment,
    title varchar(1024),
    content mediumtext, -- 正文用更长的类型
    userId int, -- 文章作者的 id
    postTime datetime -- 发布时间
);

-- 创建用户表
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');

2. Package database

2.1. Database connection DBUtil

Create the DBUtil class and put it under the model package to encapsulate the database connection operation.

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;

// 使用这个类 和数据库建立连接
public class DBUtil {
    
    
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/java_blog?characterEncoding=utf8&userSL=false";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "11111";

    private static volatile DataSource dataSource = null;

    private 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() throws SQLException {
    
    
        return getDataSource().getConnection();
    }

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

2.2. Entity Blog User

Create an entity class and use the entity class to represent a record in the database

Here we mainly create the Blog class and a User class~~

package model;

import java.sql.Timestamp;

// 每个 model.Blog 对象, 对应 blog 表里的一条记录
public class Blog {
    
    
    private int blogId;
    private String title;
    private String content;
    private int userId;
    private Timestamp postTime;
    
    // 生成它们的 get set 方法...
}
package model;

// 每个 model.User 对象, 期望能够表示 user 表中的一条记录
public class User {
    
    
    private int userId;
    private String username;
    private String password;

    // 生成它们的 get set 方法...
}

2.3. Encapsulate addition, deletion, modification and query of data

A class that provides addition, deletion, modification, and query is called 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;

// 这个类用于去封装博客表的基本操作
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, ?, ?, ?, now())";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setInt(3, blog.getUserId());
            // 3) 执行 SQL
            statement.executeUpdate();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            // 4) 关闭连接, 释放资源
            DBUtil.close(connection, statement ,null);
        }
    }

    // 2. 能够获取到博客表中的所有博客的信息 (用于在博客列表页, 此处每篇博客不一定会获取到完整的正文)
    public List<Blog> selectAll() {
    
    
        List<Blog> 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()) {
    
     // 遍历 获取每条博客 放入 blogs
                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.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
                blogs.add(blog);
            }
        } catch (SQLException e) {
    
    
            e.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 {
    
    
            connection = DBUtil.getConnection();
            String sql = "select * from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);	
            resultSet = statement.executeQuery();
            // 此处我们是使用 主键 来作为查询条件的. 查询结果, 要么是 1 , 要么是 0.
            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();
        } finally {
    
    
            DBUtil.close(connection, statement, resultSet);
        }
        return null; // 没有找到此条博客
    }

    // 4. 从博客表中, 根据博客 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 throwables) {
    
    
            throwables.printStackTrace();
        } finally {
    
    
            DBUtil.close(connection, statement, null);
        }
    }

    // 注意, 上述操作是 增删查, 没有改~~
}

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 {
    
    
                connection = DBUtil.getConnection();
                String sql = "select * from user where username = ?";
                statement = connection.prepareStatement(sql);
                statement.setString(1, username);
                resultSet = statement.executeQuery();
                // 此处 username 使用 unique 约束, 要么能查到一个, 要么一个都查不到.
                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;
    }

    // 2. 根据用户 id 来找用户信息.
    //    博客详情页, 就可以根据用户 id 来查询作者的名字, 把作者名字显示出来.
    public User selectById(int userId) {
    
    
        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();
            // 此处 username 使用 unique 约束, 要么能查到一个, 要么一个都查不到.
            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;
    }
}

The Model is done. Next, write the Controller to implement the subsequent code of the server.

Insert image description here


4. Blog list page

1. Agree on the interactive interface

For the four pages here,
respectively "agree on the front-end and back-end interaction interfaces", "write the server code", and "write the client code"

1. Start with the blog list page

This page needs to be able to display the list of blogs in the database~~

ask:

GET /blog

response:

The content here is not so much the "text" as it is the summary of the text~~
The blog list page mainly shows which blogs there are~~ Therefore, the text needs to be intercepted
here (if the text is too long, just Cut off a small part of the front)

[
	{
    
    
		blogld: 1,
		title: '这是第一篇博客',content: '这是博客正文',userld: 1,
		postTime: '2022-05-21 20:00:00'),
    },
	{
    
    
		blogld: 2,
		title: '这是第二篇博客',
		content: '这是博客正文',userld: 1,
		postTime: '2022-05-21 20:00:00'
    },
]

2. Server code BlogServlet

Create the BlogServlet class and put it in the controller package

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;

import javax.servlet.ServletException;
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();

    // 这个方法用来获取到数据库中的博客列表.
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        BlogDao blogDao = new BlogDao();
        List<Blog> blogs = blogDao.selectAll();
        // 把 blogs 对象转成 JSON 格式.
        String respJson = objectMapper.writeValueAsString(blogs);
        // 构造响应的时候 这里的这两行代码 顺序不能颠倒.
        // 如果先 write 了 body, 再设置 ContentType,设置的 ContentType就会不生效!!
        // 包括说 即使不是写 body,构造 sendRedirect 这种, 其实也是类似的情况~~
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(respJson);
    }
}

——Verify modified postTime

Start the server and Postman constructs a Get request: 127.0.0.1:8080/blog_system/blog

Display body is: []

-- 给博客表中插入点数据, 方便测试.
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 image description here

The response obtained here is slightly different from the expected effect. The time format~~what is expected is a formatted time, but actually a millisecond timestamp
is obtained . Here you need to convert the timestamp into a formatted time. (It can be done on the front end or on the back end)

Modification method: In the Blog class, modify getPostTime

	// 改为格式化时间
    // 把这里的 getter 方法给改了, 不是返回一个时间戳对象, 而是返回一个 String (格式化好的时间)
    public String getPostTime() {
    
    
        // 使用 SimpleDateFormat 来完成时间戳到格式化日期时间的转换.
        // 这个转换过程, 需要在构造方法中指定要转换的格式, 然后调用 format 来进行转换
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-mm-dd hh:mm:ss");
        return simpleDateFormat.format(postTime);
    }

Restart, construct, the result at this time:"postTime": "2022-07-26 05:07:16"


3. Client code blog_list.html

When the page loads, let the page ajaxaccess the server through , obtain the blog data in the database, and fill it into the page!

Add ajax at the end of blog_list.html:

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
    // 在页面加载的时候, 通过 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. 遍历 bo	dy, 构造出一个个的 blogDiv
                for (let blog of body) {
    
    
                    let blogDiv = document.createElement('div'); // 创建标签
                    blogDiv.className = 'blog'; // class 名
                    // -- 构造标题
                    let titleDiv = document.createElement('div');
                    titleDiv.className = 'title';
                    titleDiv.innerHTML = blog.title;
                    blogDiv.appendChild(titleDiv); // 挂入 dom 树
                    // -- 构造发布时间
                    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 = '查看全文 &gt;&gt;';
                    // 此处希望点击之后能够跳转到 博客详情页 !!
                    // 这个跳转过程需要告知服务器要访问的是哪个博客的详情页. 根据博客 id
                    a.href = 'blog_detail.html?blogId=' + blog.blogId;
                    blogDiv.appendChild(a);

                    // 把 blogDiv 挂到 dom 树上!
                    rightDiv.appendChild(blogDiv);
                }
            },
            error: function() {
    
    
                alert("获取博客列表页失败!");
            }
        });
    }
    // 调用
    getBlogList();
</script>

——Verify the order of modified blog list and load page

Start the server and visit: 127.0.0.1:8080/blog_system/blog_list.html

Brought to the blog list page, there seems to be no problem, but there are two small problems:

Question 1: The order of the blog list currently obtained is not very scientific~~

Add a blog post:

insert into blog values(null, '这是第二篇博客', '从昨天开始, 我要认真学 C++', 2, now());

Observation found that the added blog is at the bottom, which is not conducive to user access.

We need to ensure that our latest blog is at the top!!
If it is not specified in the sql order by, the order of the results queried at this time is uncertain!! Therefore, you should not rely on it before adding order by in the sql. The order of query results!!!

Maybe you find that the order of query results in the current database is certain. As you insert and delete more data,
or even update the database version, the query results at this time may be at risk of changing~~

Modify the code: BlogDao class, modify the query sql in the selectAll method

String sql = "select * from blog order by postTime desc"; // 降序 最新数据放上面

Question 2: When refreshing the page, the content trembles~~

Interruption point observation: old content (data hard-coded during testing) => new content (data checked from the database)
is interacted through the network, and it takes dozens of ms. It is normal, and the human eye can capture it. To this change~~

Just delete the old test data and that’s it~~

Insert image description here

<!-- 右侧内容详情 -->
<div class="right">
    <!-- .blog 对应一个博客 -->
    <!-- <div class="blog">
        博客标题
        <div class="title">
            我的第一篇博客
        </div>
        博客发布时间
        <div class="date">
            2022-05-05 15:00:00
        </div>
        博客摘要
        <div class="desc">
            从今天起,我要认真写博客,Lorem ipsum dolor sit amet consectetur adipisicing elit. Impedit fugit libero deleniti a distinctio exercitationem mollitia adipisci repudiandae aliquid reiciendis, quae consequatur laboriosam illum et dicta iure, error eligendi iusto?
        </div>
        ">>" 转义
        <a href="#">查看全文 &gt;&gt; </a>
    </div> -->
</div>

Guess you like

Origin blog.csdn.net/qq_56884023/article/details/125930259