サーバー版ブログ システム、フロントエンドとバックエンドの対話 1

1. 準備作業

1).Mavenプロジェクトの作成

2). 依存関係のサーブレット、ジャクソン、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). 必要なディレクトリを作成します

ここに画像の説明を挿入します

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). コードを書く

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

5). 6). パッケージのデプロイメント (スマート Tomcat に直接基づく)

コンテンツパス:/blog_system

7). ブラウザで確認する

サーバーを起動し、127.0.0.1:8080/blog_system/hello にアクセスします。

ブラウザページ: こんにちは


2. フロントエンドコードをコピーします。

ここに画像の説明を挿入します

現在、V はほぼ実装されています~~ フロントエンド ページをプロジェクトに導入し、webappディレクトリに直接コピーできます。ディレクトリを間違えないように注意してください。

これらのファイルは静的リソースとして理解できます。
後でパッケージ化してデプロイすると、これらの静的リソースもパッケージ化されてデプロイされ、後でブラウザーでアクセスできます~

C と M は、まずモデル層を実装し、データベース関連のコードを実装します。


3. データベースオペレーションコードを記述する

1. データベース/テーブル構造の作成 => (データベース設計)

設計データベースは現在のニーズに応じて設計する必要がある

ブログページ:

  1. ブログ一覧ページ:ブログの一覧を表示します。
  2. ブログ詳細ページ: ブログ一覧ページをクリックすると、上記のブログエントリがそのページにジャンプし、ブログの完全なコンテンツが表示されます。
  3. ログインページ
  4. ブログ編集ページ: editor.mdをベースにマークダウンエディタを作成しました~~ このページをベースにブログを投稿しました

ブログを保存します。「公開」をクリックすると、ブログがサーバーに公開され、
ブログを取得するために保存されます。ブログ一覧ページとブログ詳細ページで、ブログのコンテンツを取得し、ログイン認証を実行できます~~

テーブルを設計するには、要件内のエンティティ(主要な名詞)をキャプチャする必要があります。

  • ブログ テーブル。すべてのブログ データを保存するために使用されます。
  • ユーザーテーブル、ユーザーログインはこのテーブルを使用する必要があります

メインディレクトリに 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. パッケージデータベース

2.1. データベース接続 DBUtil

DBUtil クラスを作成し、モデル パッケージの下に配置して、データベース接続操作をカプセル化します。

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. エンティティブログユーザー

エンティティ クラスを作成し、そのエンティティ クラスを使用してデータベース内のレコードを表現します。

ここでは主に Blog クラスと User クラスを作成します~~

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. データの追加、削除、変更、クエリのカプセル化

追加、削除、変更、クエリを提供するクラスをDAOと呼びます。

ブログダオ
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);
        }
    }

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

ユーザーダオ
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;
    }
}

モデルが完成したら、サーバーの後続のコードを実装するコントローラーを作成します。

ここに画像の説明を挿入します


4.ブログ一覧ページ

1. インタラクティブインターフェースに同意する

ここの 4 ページについては、
それぞれ「フロントエンドとバックエンドの対話インターフェイスに関する合意」、「サーバー コードの作成」、「クライアント コードの作成」です。

1.ブログ一覧ページからスタート

このページには、データベース内のブログのリストを表示できる必要があります~~

聞く:

GET /blog

応答:

ここのコンテンツは「テキスト」というよりはテキストの要約です~~
ブログリストページは主にどのブログがあるかを示しています~~したがって、テキストは
ここで中断する必要があります(テキストが長すぎる場合は、前面の小さな部分を切り取るだけです)

[
	{
    
    
		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. サーバーコード BlogServlet

BlogServlet クラスを作成し、コントローラー パッケージに配置します。

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);
    }
}

——変更されたpostTimeを確認します

サーバーを起動すると、Postman が Get リクエストを作成します: 127.0.0.1:8080/blog_system/blog

表示本体は[]です。

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

ここに画像の説明を挿入します

ここで得られる応答は、期待される効果とは少し異なります。時間形式~~期待されるのはフォーマットされた時間ですが、実際にはミリ秒のタイム
スタンプが取得されます。ここで、タイムスタンプをフォーマットされた時間に変換する必要があります。 (これは実行できます)フロントエンドまたはバックエンド)

変更方法:BlogクラスでgetPostTimeを変更

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

再起動、構築、この時点での結果:"postTime": "2022-07-26 05:07:16"


3. クライアントコード blog_list.html

ページが読み込まれたら、 を介してページがajaxサーバーにアクセスし、データベース内のブログ データを取得して、ページに埋め込みます。

blog_list.html の最後に ajax を追加します。

<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>

——変更されたブログリストの順序を確認してページをロードする

サーバーを起動し、127.0.0.1:8080/blog_system/blog_list.html にアクセスします。

ブログ一覧ページに移動すると、一見何の問題もないように見えますが、小さな問題が 2 つあります。

質問1:現在取得されているブログリストの順序はあまり科学的ではありません~~

ブログ投稿を追加します。

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

観察の結果、追加されたブログは一番下にあることがわかり、ユーザーのアクセスにはつながりませんでした。

最新のブログが先頭にあることを確認する必要があります!!
sql で指定されていない場合order by、この時点でクエリされた結果の順序は不確かです!!したがって、order by を追加する前にそれに依存しないでください。 SQL. クエリ結果の順序!!!

おそらく、現在のデータベース内のクエリ結果の順序は確実であることがわかります。さらにデータを挿入および削除したり、
データベースのバージョンを更新したりすると、現時点でのクエリ結果が変わる可能性があります~~

コードを変更します: BlogDao クラス、selectAll メソッドのクエリ SQL を変更します。

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

質問 2:ページを更新すると、コンテンツが震えます~~

中断ポイントの観測:古いコンテンツ(テスト中にハードコーディングされたデータ) =>新しいコンテンツ(データベースからチェックされたデータ)
がネットワークを通じて対話されます。これには数十ミリ秒かかりますが、これは正常であり、人間の目で捉えることができます。この変化~~

古いテストデータを削除するだけです~~

ここに画像の説明を挿入します

<!-- 右侧内容详情 -->
<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>

おすすめ

転載: blog.csdn.net/qq_56884023/article/details/125930259