1. 程式人生 > >Spring Boot + MyBatis + Thymeleaf實現簡單留言板應用

Spring Boot + MyBatis + Thymeleaf實現簡單留言板應用

crud pro 點擊 htm drop lang data cati col

Spring Boot + MyBatis + Thymeleaf實現簡單留言板應用

本項目主要介紹使用Spring Boot + MyBatis + Thymeleaf + Bootstrap來實現一個簡單的增刪改查(CRUD)留言板應用。高階人士可以直接跳過。

源代碼:https://github.com/qingwenwei/spring-boot-crud-example

功能介紹

發表帖子、帖子列表

技術分享圖片

編輯帖子

技術分享圖片

使用Spring Initializr構建項目

Spring Initializr是一個基於web的快速構建Spring Boot項目的工具。

  1. 登錄https://start.spring.io
  2. 選擇需要的依賴。
  3. 點擊Generate Project下載項目壓縮包。

技術分享圖片

導入項目

本教程使用eclipse作為IDE。

  1. 解壓壓縮包。
  2. 導入項目,如下圖。
  3. 選擇項目根目錄(pom.xml文件所在的目錄)。

技術分享圖片

技術分享圖片

配置MySQL數據庫

在本地MySQL數據庫創建用戶名為root密碼為root的用戶。

創建一個名為crudDemoDB空的數據庫(schema),使用UTF8編碼格式是為了對漢字存儲更友好:

CREATE DATABASE crudDemoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

配置文件

maven依賴管理配置文件:pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Spring Boot應用的配置文件:src/main/resources/application.properties

# database
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/crudDemoDB?useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.initialization-mode=always

# MyBatis
mybatis.type-aliases-package=com.example.demo.model
mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

# thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML

MyBatis

resources下新建目錄mybatismybatis下新建目錄mapper。同時,添加以下兩個文件。

MyBatis的配置文件:src/main/resources/mybatis/mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="Integer" type="java.lang.Integer" />
        <typeAlias alias="Long" type="java.lang.Long" />
        <typeAlias alias="String" type="java.lang.String" />
        <typeAlias alias="HashMap" type="java.util.HashMap" />
        <typeAlias alias="Date" type="java.util.Date" />
        <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
        <typeAlias alias="ArrayList" type="java.util.ArrayList" />
        <typeAlias alias="LinkedList" type="java.util.LinkedList" />
    </typeAliases>
</configuration>

Mapper文件:src/main/resources/mybatis/mapper/PostMapper.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.dao.PostDao">

    <resultMap id="PostResultMap" type="com.example.demo.model.Post">
        <id property="id" column="post_id"/>
        <result property="title" column="post_title"/>
        <result property="content" column="post_content"/>
        <result property="creationDate" column="post_creation_date"/>
    </resultMap>

    <insert id="save">
        INSERT INTO `T_POST` (title, content, creationDate) VALUES (#{title}, #{content}, #{creationDate})
    </insert>
    
    <select id="findAll" resultMap="PostResultMap">
        SELECT
            p.id as post_id,
            p.title as post_title,
            p.content as post_content,
            p.creationDate as post_creation_date
        FROM T_POST p
        ORDER BY p.creationDate DESC
    </select>
    
    <select id="find" resultMap="PostResultMap">
        SELECT
            p.id as post_id,
            p.title as post_title,
            p.content as post_content,
            p.creationDate as post_creation_date
        FROM T_POST p
        WHERE p.id = #{postId}
    </select>
    
    <update id="update" parameterType="com.example.demo.model.Post">
        UPDATE T_POST SET 
            title = #{title},
            content = #{content}
        WHERE id = #{id}
    </update>
    
    <delete id="delete" parameterType="Long">
        DELETE FROM T_POST WHERE id = #{postId}
    </delete>
</mapper>

DAO層

Dao接口的方法需要與上面Mapper對應。

src/main/java/com/example/demo/dao/PostDao.java

package com.example.demo.dao;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.example.demo.model.Post;

@Mapper
public interface PostDao {
    
    void save(Post post);
    
    void delete(Long postId);
    
    void update(Post post);
    
    Post find(Long postId);
    
    List<Post> findAll();
    
}

實體類

src/main/java/com/example/demo/model/Post.java

package com.example.demo.model;

import java.io.Serializable;
import java.sql.Timestamp;

public class Post implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String title;
    private String content;
    private Timestamp creationDate;
    
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getTitle() {
        return title;
    }
    
    public void setTitle(String title) {
        this.title = title;
    }
    
    public String getContent() {
        return content;
    }
    
    public void setContent(String content) {
        this.content = content;
    }
    
    public Timestamp getCreationDate() {
        return creationDate;
    }
    
    public void setCreationDate(Timestamp creationDate) {
        this.creationDate = creationDate;
    }

    @Override
    public String toString() {
        return "Post [id=" + id + ", title=" + title + ", content=" + content + ", creationDate=" + creationDate + "]";
    }
    
}

業務邏輯層

接口設計

src/main/java/com/example/demo/service/PostService.java

package com.example.demo.service;

import java.util.Map;

import com.example.demo.model.Post;

public interface PostService {

    void createPost(Post post);
    
    void deletePost(Long postId);
    
    void updatePost(Post post);
    
    Map<String, Object> findPost(Long postId);
    
    Map<String, Object> findAllPosts();
    
}

具體實現

src/main/java/com/example/demo/service/impl/PostServiceImpl.java

package com.example.demo.service.impl;

import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.dao.PostDao;
import com.example.demo.model.Post;
import com.example.demo.service.PostService;

@Service("postService")
@Transactional
public class PostServiceImpl implements PostService{

    @Autowired
    private PostDao postDao;

    @Override
    public void createPost(Post post) {
        post.setCreationDate(new Timestamp(System.currentTimeMillis()));
        this.postDao.save(post);
    }

    @Override
    public void deletePost(Long postId) {
        this.postDao.delete(postId);
    }

    @Override
    public void updatePost(Post post) {
        this.postDao.update(post);
    }
    
    @Override
    public Map<String, Object> findPost(Long postId) {
        Map<String, Object> attributes = new HashMap<>();
        Post post = this.postDao.find(postId);
        attributes.put("post", post);
        return attributes;
    }
    
    @Override
    public Map<String, Object> findAllPosts() {
        Map<String, Object> attributes = new HashMap<>();
        List<Post> allPosts = this.postDao.findAll();
        // all posts
        attributes.put("posts", allPosts);
        // provide a new data transfer object
        attributes.put("postDto", new Post());
        return attributes;
    }
    
}

控制器

src/main/java/com/example/demo/controller/PostController.java

package com.example.demo.controller;

import java.util.Map;

import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.example.demo.model.Post;
import com.example.demo.service.PostService;

@Controller
public class PostController {
    
    public static final Logger logger = LoggerFactory.getLogger(PostController.class);
    
    @Autowired
    private PostService postService;
    
    /*
     * Create a new post
     */
    @RequestMapping(value = "/posts", method = RequestMethod.POST)
    public String createPost(Model model, @Valid @ModelAttribute("postDto") Post post) {
        logger.info("Creating post >> " + post);
        this.postService.createPost(post);
        return "redirect:/posts";
    }
    
    /*
     * Delete a post
     */
    @RequestMapping(value = "/posts/{postId}", method = RequestMethod.DELETE)
    public String deletePost(@PathVariable("postId") Long postId) {
        logger.info("Deleting post id:" + postId);
        this.postService.deletePost(postId);
        return "redirect:/posts";
    }
    
    /*
     * Update a post
     */
    @RequestMapping(value = "/posts", method = RequestMethod.PUT)
    public String updatePost(@Valid @ModelAttribute("postDto") Post post) {
        logger.info("Updating post >> " + post);
        this.postService.updatePost(post);
        return "redirect:/posts";
    }
    
    /*
     * List all posts
     */
    @RequestMapping(value = "/posts", method = RequestMethod.GET)
    public String listAllPosts(Model model) {
        logger.info("Litsing all posts...");
        Map<String, Object> attributes = this.postService.findAllPosts();
        model.addAllAttributes(attributes);
        return "post-index";
    }
    
    /*
     * Display post details
     */
    @RequestMapping(value = "/posts/{postId}", method = RequestMethod.GET)
    public String displayPostDetails(Model model, @PathVariable("postId") Long postId) {
        logger.info("Displaying post details >> postId: " + postId);
        Map<String, Object> attributes = this.postService.findPost(postId);
        model.addAllAttributes(attributes);
        return "post-details";
    }
    
}

Spring Boot入口類

src/main/java/com/example/demo/DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

SQL腳本

Spring Boot在運行時會檢查在resources目錄下是否有名為schema.sqldata.sql的兩個SQL腳本文件,並且在數據庫連接池建立後執行這兩個文件。

src/main/resources/schema.sql

DROP TABLE IF EXISTS `T_POST`;

CREATE TABLE `T_POST`(
  `id`                  BIGINT          NOT NULL AUTO_INCREMENT,
  `title`               VARCHAR(255)    NOT NULL,
  `content`             TEXT,
  `creationDate`        DATETIME,
  PRIMARY KEY (`id`)
);

src/main/resources/data.sql

INSERT INTO `T_POST` (title, content, creationDate) VALUES (‘測試標題1‘, ‘測試正文1‘, ‘2018-01-23‘);
INSERT INTO `T_POST` (title, content, creationDate) VALUES (‘測試標題2‘, ‘測試正文2‘, ‘2018-01-25‘);

前端頁面

前端頁面代碼在這裏就不一一貼出來了。感興趣的朋友可以下載項目代碼來看看。

帖子列表頁面

src/main/resources/templates/post-index.html

帖子內容

src/main/resources/templates/post-details.html

通過CDN導入bootstrap和jQuery

src/main/resources/templates/head.html

源代碼

源代碼:https://github.com/qingwenwei/spring-boot-crud-example

Spring Boot + MyBatis + Thymeleaf實現簡單留言板應用