There is no way to build a personal blog project implementation based on the SpringBoot framework that is neither from scratch nor prenatal education (with code)

Posted by trippyd on Mon, 17 Jan 2022 09:23:55 +0100

The code of using SpringBoot framework to build personal blog is attached at the end of the article

1. Download environment

  first, on the official website https://start.spring.io/ Download a template locally, use spring boot version 2.4.8, java 8, download the jar package, and import Spring Boot DevTools (hot deployment), Lombok (quick notes) and Spring Web (required for network projects); The next time you use it, unzip it and change the file name

2. Installation dependency

  mysql and mybatis dependencies were not installed when we created them. We need to install a new plug-in to facilitate adding new dependencies,
First, go to Settings, then click plugins, search for EditStarters in the Marketplace, then install, and restart IDEA after installation; If the Marketplace cannot be refreshed, just restart the IDEA; At this point, we enter POM XML, right-click this file and click generate -- > Edit starters; If there is no error, click OK. If there is an error, saying that the Spring Boot cannot be found, re introduce the load (build) project environment in Maven in the upper right corner; Then search mysql and mybatis, and reload Maven

3. Configure application Properties file

debug = false
## Log save path
#logging.file.path = logs/
#logging.logback.rollingpolicy.max-file-size=10MB

# Set log level
logging.level.root = info

# Set port number
server.port=8080

# Database connection address
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=true
spring.datasource.name=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

# mybatis mapper path
mybatis.mapper-locations=classpath:mapper/**Mapper.xml

# Open the sql execution log of mybatis
logging.level.com.example.demo.mapper=debug

   the mybatis mapper path is used to configure * * mapper XML file. The configuration file should be placed under resources to create a mapper file. The function of this file is to get the mapping of the specific methods of database operation. In this way, when we write the specific operation methods under the java folder, we only need to write an interface of this class of methods, and the specific implementation part of this interface is placed in Mapper.xml of the corresponding class XML file, which will be described in detail in the following examples.

  • The function of opening the sql execution log of mybatis is to make COM example. demo. The programs and forms in the mapper folder print logs at the debug level

4. The static pages of all web pages are fully prepared, and the functions will be implemented later according to the following order

  • (0)index.html: navigation page
  • (1)login.html: landing page;
  • (2)regin.html: registration page; reg_err.html: registration failure page; reg_succerss.html: Registration success page
  • (3)myblog.list.html: my article page
  • (4)blog_content.html: article content page*
  • (5)blog_edit: add article page

5. Some tool classes to be used

(1) Template class and database
   according to our experience in using servlet s to do projects, we know that login requires user information, so we need a model template class to store all user information (User.java) or all article information (ArticleInfo.java); Secondly, how to map to the front-end url and operate on the back-end is the problem that the controller class needs to solve. The user related operations are written under the controller class · in usercontroller The Java class lists various specific operation methods. Similarly, the specific operation methods of the article are written in the articleinfocontroller under the controller class Java.
   at present, we want to realize the user login function. First, build the database. The database template is as follows. Import the following sql code into mysql and execute it into the database:

-- Create database
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8;

-- Use data
use mycnblog;

-- Create table[User table]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
);

-- Create article table
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime datetime default now(),
updatetime datetime default now(),
uid int not null,
rcount int not null default 1,
`state` int default 1
);

-- Add test data to the article
insert into articleinfo(title,content,uid)
values('Java','Java text',1);

  after we have this database locally, we need to create a user in the model template class according to the userinfo table and articlinfo table in the database Java and articleinfo Java two template classes. The member variable names in these two template classes must be the same as the attribute names in the database to facilitate subsequent communication
  User information template class

package com.example.demo.model;
import lombok.Data;
import java.util.List;
@Data // With this annotation, we don't have to write private get and set methods
public class User {
    private int id;
    private String username;
    private String password;
    private String photo;
    private List<ArticleInfo> alist; // To know how many articles the current user has, you need to define the contents of the article table here
}

  ArticleInfo article content template class

package com.example.demo.model;
import lombok.Data;
import java.util.Date;
@Data
public class ArticleInfo {
    private int id;
    private String title;
    private String content;
    private Date createtime;
    private Date updatetime;
    private int uid;
    private int rcount;
    private String state;
    private User user;
}

(2) Some related configuration files (config.java class)
①: AppFinal.java -- write the constant that will not change in the project, that is, the variable modified by final

package com.example.demo.config;
// Save global constants
public class AppFinal {
// The key name of the session after the user logs in
public static final String USERINFO_SESSIONKEY = "userinfo";
// Path for storing pictures
public static final String IMAGE_PATH = "/upload/";
}

②: AppConfig.java - in this class, we configure the url usage rules and interception rules (the interceptor needs to be defined separately under the config class)

package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* In this class, we configure the usage specification of the url, and can request the intercepted address by configuring the interceptor
* */
@Configuration  // Comments representing the configuration file
public class AppConfig implements WebMvcConfigurer {// To implement custom URLs and connection rules, it is also important to inherit WebMvcConfigurer
    // Custom url prefix
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("api" , c -> true);
        // Used here λ Expression, which means that we should add api when inputting the URLs of all pages on the web page
        // C - > true means that the configurer object is true. When it is false, it means that it is not added
    }
    
    // Custom interceptor rules
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**") // Block all interfaces
                .excludePathPatterns("/api/user/login") // Do not block login interface
                .excludePathPatterns("/api/user/reg") // Do not intercept registered interfaces
                .excludePathPatterns("/login.html") // Do not block login pages
                .excludePathPatterns("/regin.html") // Do not block registration pages
                .excludePathPatterns("/**/**.html") // Do not block registration pages
                .excludePathPatterns("/**/*.css")
                .excludePathPatterns("/**/*.js")
                .excludePathPatterns("/**/*.jpg")
                .excludePathPatterns("/reg_success.html")
                .excludePathPatterns("/reg_err.html")
                .excludePathPatterns("/**/*.png");
    }
}

③: LoginInterceptor.java: -- login interceptor. When the user does not log in (there is no valid Session information on the web page), he cannot access any other page

package com.example.demo.config;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor { // HandlerInterceptor is often used to implement interception, so we need to inherit it
    // The user-defined interception method returns a boolean value
    // true indicates that you can access the back-end interface, and false indicates that you do not have permission to access the back-end interface
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Judge whether the Session has a value
        HttpSession session = request.getSession(false); // false means no new session will be created
        if(session != null &&
            session.getAttribute(AppFinal.USERINFO_SESSIONKEY) != null){
            // Indicates that the user has logged in
            return true;
        }
        return false;
    }
}

④: ErrorAdvice.java -- customize the exception and package the output exception into the desired format (here, package it into json string format to return the error to the front end)

package com.example.demo.config;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
@ControllerAdvice // Specifies the return data format when an exception occurs
public class ErrorAdvice {
    @ExceptionHandler(RuntimeException.class) // Customize the format of runtime exceptions
    @ResponseBody
    public Object err2(Exception e){
        HashMap<String, Object> map = new HashMap<>();
        map.put("status", "-2");
        map.put("data", "");
        map.put("msg", e.getMessage());
        return map;
    }
    @ExceptionHandler(NullPointerException.class) // Custom output format for null pointer exceptions
    @ResponseBody
    public Object err3(Exception e) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("status", "-3");
        map.put("data", "");
        map.put("msg", "Null pointer exception");
        return map;
    }
    @ExceptionHandler
    @ResponseBody
    public Object err(Exception e) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("status", "-1");
        map.put("data", "");
        map.put("msg", e.getMessage());
        return map;
    }
}

⑤ : MyResponseBodyAdvice -- package the data returned to the front end (this is almost the same as the template in project use)

package com.example.demo.config;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.HashMap;
@ControllerAdvice // The class with enhanced return format should inherit ResponseBodyAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
    // Both methods of ResponseBodyAdvice must be overridden
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object o,
                                  MethodParameter methodParameter,
                                  MediaType mediaType,
                                  Class aClass,
                                  ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("status", 0);  // ID returned to the front end
        map.put("data", o);     // o object is the data object part
        map.put("msg","");
        return map;
    }
}

(3) In Spring, if you want to add, delete, check and modify the database by connecting to the database, you don't need to use JDBC like the servlet project. MyBatis is used here. At the beginning of the application In the properties file, we have defined the relevant information of the native database. There is a property of the mybatis mapper path. Here, we need to use the Mapper mapping method in Spring to modify the database.
   the specific logic is as follows: Taking registration as an example, if the routing address of the regin method in the UserController is "/ reg", there needs to be a new mapper class under the folder at the same level as the controller, and there are subclass interfaces for specific operations under the mapper class. Here, it is the UserMapper interface. Under UserMapper, only methods are written, but no specific implementation is written, By creating a new folder UserMapper. Com under the resources folder XML, which is used to configure the specific database operation logic. It is similar to UserMapper The java interface is interworking in UserMapper Java defines the interface method to mapper XML file.
  in usermapper Write mapped interface in java interface class

package com.example.demo.mapper;
import com.example.demo.model.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper // The configuration file used to map the mapper
public interface UserMapper {
    // Add user (registration function)
    public int addUser(User user);
}

  go to usermapper Write SQL statements to modify the database in XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">

    <insert id="addUser" parameterType="com.example.demo.model.User"
            useGeneratedKeys="true" keyProperty="id" keyColumn="id"><!-->parameterType Defines the parameter type to be returned. Here, the user class is returned<!-->
            <!-->useGeneratedKeys by true The following two parameters refer to the parameter name and the attribute name in the data table<!-->
            insert into userinfo(username, password,photo)
            values (
                #{username},#{password},#{photo}
        )
    </insert>
</mapper>

5. Realize the back-end registration function

  (1) finish the back-end part first
  next, we need to create a user before registering a user Java, the userinfo attribute in all databases is not used here, which is all we need to register users
    next, you need the controller class as an intermediary to pass the url address and create usercontroller java

package com.example.demo.controller;
import com.example.demo.config.AppFinal;
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Controller // As a Spring project, you need to use the Controller annotation to host the current class to Spring. If this annotation is not used, the class will not run when Spring runs
@RequestMapping("/user") // The function is to map the address of the url
@Slf4j  // Comments used to print logs in lombok
public class UserController {
    // From application Get the value of the myimgpath parameter from the properties configuration file
    @Value("${myimgpath}")
    private String imgpath;
    @Resource // Get the mapping object of usermapper so that you can modify the database according to the mapper
    private UserMapper userMapper;
    // Realize the registration function
    @RequestMapping("/reg") // Step 1: the url used to establish the connection must not be forgotten
    public String regin(String username, String password,
                        @RequestParam MultipartFile file) throws IOException { // The parameters in this line are used to read the avatar image uploaded by the user
        // Step 2: todo: non null validation
        // Step 3: dynamically obtain the path of the current project
        //      This is because when the project is arranged on the server, the path must be different from the local path. We need to save the image under the avatar image path specified in the current project
        String path = imgpath;
        path += AppFinal.IMAGE_PATH;
        log.info("path:" + path);  // Output image file storage path through log
        // Step 4: get the file type name (file suffix) of the picture we sent and generate the file name
            // Fetch file type
        String fileType = file.getOriginalFilename(); // This step gets the full file name
        fileType = fileType.substring(fileType.lastIndexOf("."));// Take The latter, such as img What JPG gets here is jpg
            // Get the globally unique file name generated
        String fileName = UUID.randomUUID().toString() + fileType;
            // Splice the file names together and save them on the server
        file.transferTo(new File(path + fileName));
        // Step 5: store the user information in the database of the server
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        user.setPhoto(AppFinal.IMAGE_PATH + fileName); // Set the address of the avatar
        int result = userMapper.addUser(user);
        if(result > 0){
            // Operation succeeded
            return "redirect:/reg_success.html"; // Redirect to registration success page
        }else {
            return "redirect:/reg_err.html";
        }
    }
}

(2) Solve the front-end part - registration page front-end code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Registration page</title>
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/login.css">
</head>
<body>
<form id="form1" action="/api/user/reg" method="post" enctype="multipart/form-data">
    <!-- navigation bar -->
    <div class="nav">
        <img src="img/logo2.jpg" alt="">
        <span class="title">My blog system</span>
        <!-- Used to occupy the middle position -->
        <span class="spacer"></span>
        <a href="blog_list.html">homepage</a>
        <a href="blog_edit.html">Blog</a>
        <a href="login.html">land</a>
        <!-- <a href="#"> logout < / a > -- >
    </div>
    <!-- Edition Center -->
    <div class="login-container">
        <!-- Middle login box -->
        <div class="login-dialog">
            <h3>land</h3>
            <div class="row">
                <span>user name</span>
                <input type="text" id="username" name="username">
            </div>
            <div class="row">
                <span>password</span>
                <input type="password" id="password" name="password">
            </div>
            <div class="row">
                <span>Confirm password</span>
                <input type="password" id="password2" name="password2">
            </div>
            <div class="row">
                <span>head portrait</span>
                <input type="file" id="file" name="file">
            </div>
            <div class="row">
                <button id="submit" onclick="mysub()">Submit</button>
            </div>
        </div>
    </div>
</form>
</body>
<script src="js/jquery.min.js"></script>
<script>
    function mysub() {
        // todo: non null test
        // Verify whether the password and password are consistent
        var password1 = jQuery("#password").val().trim();
        var password2 = jQuery("#password2").val().trim();
        if (password1 != "" && password2 != "" &&
            password1 != password2) {
            alert("The two passwords are inconsistent. Please re-enter!");
            return false;
        }
        // Submit form (jquery built-in method)
        jQuery("#form1").submit();
    }
</script>
</html>

  registration success prompt page

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>login was successful</h1>
<a href="login.html">Click Add to login page</a>
</body>
</html>

  registration failure prompt page

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>login has failed</h1>
<a href="regin.html">Click Add registration page</a>
</body>
</html>

   note: if an error is reported after the registration is completed, the error content is that the database cannot be connected. The reason may be that there are some problems with the code format copied and pasted in the past. Delete the user name and password and type it by hand.

6. Realize login function

  similarly, we now have the user's template class, so in usercontroller Write the communication front and back end transfer method of url in Java, and then in usermapper Create a new mapping interface for the query method in UserMapper.java Just write the method of querying the database in XML
   (1) first, in usercontroller Define the url and query the database in Java and return the data to the front-end verification method (only the login function is pasted here)

@RequestMapping("/login")
@ResponseBody // With this annotation, the JSON string is returned to the front end
public Object login(User user, HttpServletRequest request){
   // Query according to the user name and password sent by the front end
   User user2 = userMapper.getUserByNameAndPassword(user.getUsername(), user.getPassword());
   if(user2 == null){
       user2 = user;
   }else {
       // Login successfully added session information
       HttpSession session = request.getSession();
       // Store session information
       session.setAttribute(AppFinal.USERINFO_SESSIONKEY, user2);
   }
   return user2;
}

  (2) in usermapper Java add mapping interface - login function (getUserByNameAndPassword)

package com.example.demo.mapper;
import com.example.demo.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper // The configuration file used to map the mapper
public interface UserMapper {
    // Add user (registration function)
    public int addUser(User user);
    // According to the user name and password from the front end, go to the back-end server to query user information and verify login (login function)
    public User getUserByNameAndPassword(@Param("name") String username, String password); // This Param("name") replaces username with name when passing username to the backend
}

  (3) in usermapper XML to write specific query database implementation - login function (id = "getUserByNameAndPassword")
It should be noted that the type we return here does not directly return an object of a User template class like registration, but is returned as an object of map type through the mapping relationship between the attribute name of the class and the list of the database

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="com.example.demo.model.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="photo" property="photo"/>
    </resultMap>
    <insert id="addUser" parameterType="com.example.demo.model.User"
            useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert into userinfo(username,password,photo)
        values(
            #{username},#{password},#{photo}
        )
    </insert>
    <select id="getUserByNameAndPassword" parameterType="java.lang.String"
            resultMap="BaseResultMap"><!--> Here you create a map Format, which is used to map the attribute name of the class and the column name of the database. We define it in xml The beginning of the file <!-->
        select * from userinfo where username=#{name} and password=#{password}
    </select>
</mapper>

  (4) when we land successfully, the European Commission will jump to mybolg_list.html page, here you can write a static page instead, indicating that the login is successful

7. Personal article list display function

  now you need to get personal article information, so you need both user information and article information, so you will use joint table query here. Here, on the contrary, we first write the front-end page and write the back-end operation according to the front-end page
   (1) first put the front-end page here, and then write the Controller according to the url address defined in the front-end page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog list</title>
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/blog_list.css">
</head>
<body>
<!-- navigation bar -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">My blog system</span>
    <!-- Used to occupy the middle position -->
    <span class="spacer"></span>
    <a href="blog_list.html">homepage</a>
    <a href="blog_edit.html">Blog</a>
    <!-- <a href="#"> logout < / a > -- >
</div>
<!-- Edition Center -->
<div class="container">
    <!-- Left personal information -->
    <div class="container-left">
        <div class="card">
            <img id="photoimg" src="" class="avtar" alt="">
            <h3 id="username"></h3>
            <a href="http:www.github.com">github address</a>
            <div class="counter">
                <span>article</span>
            </div>
            <div class="counter">
                <span id="acount"></span>
            </div>
        </div>
    </div>
    <!-- Right content details -->
    <div class="container-right" id="cdiv">
        <!-- Here through id="cdiv" To get the content passed from the back end to the front end -->
    </div>
</div>
</body>
<script src="js/jquery.min.js"></script>
<script src="js/mytools.js"></script>  <!-- Here you will get url The parameters are placed in the js In code -->
<script>
    // Get the current user id
    var uid = getParamValue("uid");
    if (uid != null) {
        jQuery.getJSON("/api/user/getalist", {"uid": uid}, function (data) {
            if (data != null && data.status == 0) {
                // User information
                var userinfo = data.data;
                // Article list
                var alist = userinfo.alist;
                // Set user name
                jQuery("#username").html(userinfo.username);
                // Set Avatar
                jQuery("#photoimg").attr("src", userinfo.photo);
                // Format article
                jQuery("#acount").text(alist.length);
                var contentHtml = "";
                // Set article list
                for (var i = 0; i < alist.length; i++) {
                    contentHtml += "<div class=\"blog\">\n" +
                        "            <div class=\"title\">" + alist[i].title + "</div>\n" +
                        "            <div class=\"date\">" +
                        alist[i].createtime.substring(0, alist[i].createtime.indexOf("T"))
                        + "</div>\n" +
                        "            <div class=\"desc\">\n" +
                        alist[i].content +
                        "            </div>\n" +
                        "            <a href=\"blog_content.html?id=" + alist[i].id + "&acount=" +
                        alist.length
                        + "\" class=\"detail2\">View full text &gt;&gt;</a>\n" +
                        "            <a href=\"javascript:mydel(" + alist[i].id + ")\" class=\"detail2\">delete</a>\n" +
                        "        </div>";
                }
                jQuery("#cdiv").html(contentHtml);
                // alert("user:" + userinfo.username + ", number of published articles:" + alist.length);
            }
        });
    }
    // Delete event
    function mydel(id) {
        if (confirm("Are you sure you want to delete")) {
            // Access the backend interface to delete articles
            jQuery.getJSON("/api/art/del", {"id": id}, function (data) {
                if (data != null && data.status == 0 &&
                    data.data == 1) {
                    // Delete succeeded
                    alert("Congratulations: delete data successfully!");
                    // Refresh page
                    location.href = location.href;
                } else {
                    alert("Sorry: the operation failed. Please try again!");
                }
            });
        }
    }
</script>
</html>

   (2) first analyze the first jQuery -- jQuery Getjson ("/ API / user / getalist" ---- in a method, we want to intercept and display the article section displayed in the front end, but because the article we save is in markdown format, it will not display pure text in the front end, so we need to add an HtmlText class to help us delete the character in markdown format

package com.example.demo.tool;
import java.util.regex.Pattern;
public class HtmlText {
    public static String Html2Text(String inputString) {
        String htmlStr = inputString;
        String textStr = "";
        java.util.regex.Pattern p_script;
        java.util.regex.Matcher m_script;
        java.util.regex.Pattern p_style;
        java.util.regex.Matcher m_style;
        java.util.regex.Pattern p_html;
        java.util.regex.Matcher m_html;
        java.util.regex.Pattern p_html1;
        java.util.regex.Matcher m_html1;
        try {
            String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>"; //Define the regular expression of script {or < script [^ >] *? > [\ \ s \ \ S] *? < \ \ / script >}
            String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>"; //Define the regular expression of style {or < style [^ >] *? > [\ \ s \ \ S] *? < \ \ / style >}
            String regEx_html = "<[^>]+>"; //Regular expressions that define HTML tags
            String regEx_html1 = "<[^>]+";
            p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
            m_script = p_script.matcher(htmlStr);
            htmlStr = m_script.replaceAll(""); //Filter script tags

            p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
            m_style = p_style.matcher(htmlStr);
            htmlStr = m_style.replaceAll(""); //Filter style tags

            p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
            m_html = p_html.matcher(htmlStr);
            htmlStr = m_html.replaceAll(""); //Filter html tags

            p_html1 = Pattern.compile(regEx_html1, Pattern.CASE_INSENSITIVE);
            m_html1 = p_html1.matcher(htmlStr);

            htmlStr = m_html1.replaceAll(""); //Filter html tags
            textStr = htmlStr;
        } catch (Exception e) {
            System.err.println("Html2Text: " + e.getMessage());
        }
        return textStr;//Returns a text string
    }
}

    the url address passed to the backend here is / user/getalist, so here we need to create a route ("/ getlist") under the UserController class

@RequestMapping("/getalist")
@ResponseBody
public Object getFullUser(int uid){
   User user = userMapper.getFullUser(uid);
   // Because the general information of all articles is shown here, a part of the text of the article should be deleted
   List<ArticleInfo> list = user.getAlist();
   for (ArticleInfo item : list){
       // Remove html tags
       String desc = HtmlText.Html2Text(item.getContent());
       // Intercept string
       if(desc.length() > 64){
           desc = desc.substring(0, 64) + "...";
       }
       item.setContent(desc);
   }
   user.setAlist(list);
   return user;
}

   after writing, it is found that the return value is null. After careful inspection, it is found that it is usermapper For the problem of XML configuration, since the associated table query is used here, the following should be added in the. Here, by building the linked list query, you can see the BaseResultMap in ArticleInfoMapper when the resultMap location map is returned in the collection. Therefore, you need to build an XML configuration file under the mapper folder, called ArticleInfoMapper xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="com.example.demo.model.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="photo" property="photo"/>
        <collection property="alist" columnPrefix="a_"
                    resultMap="com.example.demo.mapper.ArticleInfoMapper.BaseResultMap">
        </collection>
    </resultMap>
. . . . . 
. . . . . 
</mapper>

  create a new articleinfomapper XML file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ArticleInfoMapper">
    <resultMap id="BaseResultMap" type="com.example.demo.model.ArticleInfo">
        <id property="id" column="id"></id>
        <result property="title" column="title"></result>
        <result property="content" column="content"></result>
        <result property="createtime" column="createtime"></result>
        <result property="updatetime" column="updatetime"></result>
        <result property="uid" column="uid"></result>
        <result property="rcount" column="rcount"></result>
        <result property="state" column="state"></result>
        <association property="user"
                     columnPrefix="u_"
                     resultMap="com.example.demo.mapper.UserMapper.BaseResultMap">
        </association>
    </resultMap>
</mapper>

   after completing the supplement of these two steps, the associated table query can work and return the information to the front end

8. Personal article deletion function

  in the front page of step 7, we see the function of deleting articles that have been written. The interactive route written there is jQuery getJSON("/api/art/del",
  therefore, you need to create another Controller class called articleinfocontroller Java, write methods to delete articles in this class

package com.example.demo.controller;
import com.example.demo.mapper.ArticleInfoMapper;
import com.example.demo.model.ArticleInfo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController // Combination of ResponseBody + Controller
@RequestMapping("/art")
public class ArticleInfoController {
    @Resource // Import mapping relation interface
    private ArticleInfoMapper articleInfoMapper;
    // Delete article by article id
    @RequestMapping("/del")
    public int delById(@RequestParam int id){
        return articleInfoMapper.delById(id);
    }
}

  then create the articleinfocontroller under the mapper interface class Java interface class

package com.example.demo.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ArticleInfoMapper {
    // Delete article by article id
    public int delById(int id);
}

  finally go to articleinfocontroller Write specific delete sql statements under the XML configuration file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ArticleInfoMapper">
    <resultMap id="BaseResultMap" type="com.example.demo.model.ArticleInfo">
        <id property="id" column="id"></id>
        <result property="title" column="title"></result>
        <result property="content" column="content"></result>
        <result property="createtime" column="createtime"></result>
        <result property="updatetime" column="updatetime"></result>
        <result property="uid" column="uid"></result>
        <result property="rcount" column="rcount"></result>
        <result property="state" column="state"></result>
        <association property="user"
                     columnPrefix="u_"
                     resultMap="com.example.demo.mapper.UserMapper.BaseResultMap">
        </association>
    </resultMap>

    <delete id="delById" parameterType="java.lang.Integer">
        delete from articleinfo where id=#{id}
    </delete>
</mapper>

9. View Article Details

&  click the view full text of the personal list to jump to the article details page from mybolg_ list. One sentence in the HTML code is to jump to the blog of the article details page_ content. HTML, as follows

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog body</title>
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/blog_content.css">
</head>
<body>
<!-- navigation bar -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">My blog system</span>
    <!-- Used to occupy the middle position -->
    <span class="spacer"></span>
    <a href="blog_list.html">homepage</a>
    <a href="blog_edit.html">Blog</a>
    <a href="login.html">land</a>
</div>
<!-- Edition Center -->
<div class="container">
    <!-- Left personal information -->
    <div class="container-left">
        <div class="card">
            <img id="phtotimg" src="" class="avtar" alt="">
            <h3 id="name"></h3>
            <a href="http:www.github.com">github address</a>
            <div class="counter">
                <span>article</span>
            </div>
            <div class="counter">
                <span id="acount">0</span>
            </div>
        </div>
    </div>
    <!-- Right content details -->
    <div class="container-right">
        <div class="blog-content">
            <!-- Blog title -->
            <h3 id="title"></h3>
            <!-- Blog time -->
            <div id="createtime" class="date"></div>
            <!-- Blog body -->
            <div id="content">
            </div>
        </div>
    </div>
</div>
</body>
<script src="js/jquery.min.js"></script>
<script src="js/mytools.js"></script>
<script>
    // 1. Get the article id of the current page
    var id = getParamValue("id");
    if (id != null && id > 0) {
        // 2. Request the backend interface to query articles and user information
        jQuery.getJSON(
            "/api/art/detail",
            {"id": id},
            function (data) {
                if (data != null && data.status == 0 &&
                    data.data.id > 0) {
                    // The article has been queried normally
                    // Dynamic assignment of article and user information
                    jQuery("#phtotimg").attr("src", data.data.user.photo);
                    jQuery("#name").html(data.data.user.username);
                    // Take the number of articles published from the url
                    jQuery("#acount").text(getParamValue("acount"));
                    // Set article title
                    jQuery("#title").html(data.data.title);
                    // Set article publishing time
                    var ctime = data.data.createtime; // 2021-07-13T..
                    ctime = ctime.substring(0, ctime.indexOf("T"));
                    jQuery("#createtime").html(ctime);
                    // Set the text information of the article
                    jQuery("#content").html(data.data.content);
                } else {
                    alert("Sorry, the operation failed, please try again!");
                }
            }
        );
    }
</script>
</html>

  where jQuery Getjson ("/ API / Art / detail"), so add a route in ArticleInfoController.java

// Query article details
@RequestMapping("/detail")
public ArticleInfo detail(@RequestParam int id) {
    return articleInfoMapper.detail(id);
}

  then configure articleinfomapper Java interface and articleinfomapper XML concrete sql statement

package com.example.demo.mapper;
import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ArticleInfoMapper {
    // Delete article by article id
    public int delById(int id);
    // View article details by article id
    public ArticleInfo detail(int id);
}
<select id="detail" resultType="com.example.demo.model.ArticleInfo">
    select a.*,u.username u_username,u.photo u_photo from articleinfo a
    left join userinfo u on a.uid=u.id where a.id=#{id}
</select>

10. Add article

  also put out the front page first

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog editor</title>
    <!-- Introduce your own style -->
    <link rel="stylesheet" href="css/conmmon.css">
    <link rel="stylesheet" href="css/blog_edit.css">
    <!-- introduce editor.md Dependence of -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css"/>
    <script src="js/jquery.min.js"></script>
    <script src="editor.md/editormd.js"></script>
</head>
<body>
<!-- navigation bar -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">My blog system</span>
    <!-- Used to occupy the middle position -->
    <span class="spacer"></span>
    <a href="blog_list.html">homepage</a>
    <a href="blog_edit.html">Blog</a>
    <a href="login.html">land</a>
    <!-- <a href="#"> logout < / a > -- >
</div>
<!-- Edit box container -->
<div class="blog-edit-container">
    <!-- Title editing area -->
    <div class="title">
        <input id="title" name="title" type="text" placeholder="Write the title of the article here">
        <button onclick="mysub()">Publish articles</button>
    </div>
    <!-- Create editor label -->
    <div id="editorDiv"></div>
</div>
<script>
    // Initialize editor
    var editor = editormd("editorDiv", {
        // The dimensions here must be set here The set style will be automatically overwritten by editormd
        width: "100%",
        // Height 100% means as high as the parent element To remove the height of the title editing area based on the parent element
        height: "calc(100% - 50px)",
        // Initial content in editor
        markdown: "# Write a blog here ",
        // Specify editor MD dependent plug-in path
        path: "editor.md/lib/",
        saveHTMLToTextarea: true //
    });
    // Submit
    function mysub() {
        // title
        var title = jQuery("#title").val();
        // text
        var content = editor.getHTML();
        // todo: non null test
        // Submit the content of the current page to the backend
        jQuery.getJSON("/api/art/add",
            {
                "title": title,
                "content": content
            },
            function (data) {
                if (data != null && data.data != null &&
                    data.data.id >= 0) {
                    // Successfully added article
                    alert("Congratulations on adding the article");
                    if (confirm("Do you want to continue adding articles?")) {
                        // The page needs to be refreshed
                        location.href = location.href;
                    } else {
                        location.href = "myblog_list.html?uid=" + data.data.uid;
                    }
                } else {
                    alert("Sorry, the operation failed, please try again!");
                }
            });
    }
</script>
</body>
</html>

  similarly, look at the parameter passed by jQuery: jQuery Getjson ("/ API / Art / add"), you can see that you want to create a method with route ("/ add") under ArticleInfoController.java

// Add article
@RequestMapping("/add")
public ArticleInfo add(@RequestParam String title,
                       @RequestParam String content,
                       HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    // Before adding articles, verify the logged in user information before adding articles
    Object u = null;
    if (session == null ||
            (u = session.getAttribute(AppFinal.USERINFO_SESSIONKEY)) == null) {
        return null;
    }
    User user = (User) u;
    ArticleInfo articleInfo = new ArticleInfo();
    articleInfo.setTitle(title);
    articleInfo.setContent(content);
    articleInfo.setUid(user.getId());
    int result = articleInfoMapper.add(articleInfo);
    return articleInfo;
}

  then add articleinfomapper Java interface, and then go to articleinfomapper Writing sql statements in XML

package com.example.demo.mapper;
import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ArticleInfoMapper {
    // Delete article by article id
    public int delById(int id);
    // View article details by article id
    public ArticleInfo detail(int id);
    // Add article
    public int add(ArticleInfo articleInfo);
}
<insert id="add">
    insert into articleinfo(title,content,uid) values(
        #{title},#{content},#{uid}
    )
</insert>

  the code link is attached below. After the environment dependency is configured, the code can be used. Remember to modify the username, password and database name of the local database
Link: https://pan.baidu.com/s/1rJXasGWJhP96y4TbmW1WbA
Extraction code: gi1o