1. 程式人生 > >SpringBoot技術棧搭建個人博客【項目準備】

SpringBoot技術棧搭建個人博客【項目準備】

mapper 工作 速度 column 關於 domain 表達 dds 今天

技術分享圖片

前言:很早之前就想要寫一個自己的博客了,趁著現在學校安排的實習有很多的空檔,決定把它給做出來,也順便完成實習的任務(搞一個項目出來...)

需求分析

總體目標:設計一套自適應/簡潔/美觀/易於文章管理發布的一個屬於我個人的博客,最後一頁能展示我個人的簡歷,因為大三快結束了馬上就該去找工作了...哦忘了,最重要的還是要支持Markdown才行,因為已經習慣了...

前端需求分析

首先,前端的頁面要求是:

  • ①簡潔/美觀——個人很喜歡像Mac那樣的簡潔風,越簡單越好,當然也得好看;
  • ②最好是單頁面——單頁面的目的一方面是為了簡潔,另一方面也是為了實現起來比較簡單;
  • ③自適應——至少能適配常見的手機分辨率吧,我可不希望自己的博客存在顯示差異性的問題;

然後,思考了一下可能出現的頁面
技術分享圖片

1)首頁:

  • 最新的文章——我可以搞一個輪播之類的東西用來顯示最新的幾篇博文;
  • 頂部導航——導航欄可以要有,而且可以提出來搞成通用的;
  • 聯系方式——首頁最好再有能一眼找到我的聯系方式,比如簡書/博客園/微信公眾號之類的;
  • 時間——摁,時間;

2)文章頁:

  • 分類欄——左側應該有文章的分類,記得要有一個全部文章;
  • 文章列表——分類欄的右邊就應該是該分類下的所有文章;
  • 分頁欄——考慮到現在我的Java Web分欄下的文章已經有那麽多了,還是有必要搞個分頁;

3)簡歷頁:

這是預留的頁面,到時候用來顯示個人的簡歷;

4)關於頁:

用來介紹項目的搭建編寫過程,還有使用的技術棧啊之類的,然後留下個人的聯系方式,Nice;

5)留言頁:

因為是個人的博客,所以我並不想要限制看官們留言的權利,我希望他們能自己能定義用於顯示的用戶名,但是需要填寫一個Email,不然搞得我不能回復,那搞個啥...當然也可以不留Email,也就是不希望得到回復唄(那可能有些留言會讓我難受死吧..思考...)...

後臺需求分析:

最初的思考是這樣的:

技術分享圖片

後來一想,文章置頂這個都給忘了...然後發現其實有一個很關鍵的問題就是Markdown的文章應該怎樣保存?一開始還是想要保存為.md文件保存在服務器的硬盤上的,但想想直接保存在數據庫裏也不錯,省的麻煩,而且我很明確一點的是:我並不會直接在博客上寫Markdown,因為有許許多多成熟的產品能讓我寫的舒心的多,我沒必要去搞這麽麻煩復雜繁瑣,而且不一定好,所以我只需要用博客來展示我寫的Markdown格式的博文

就好了,Nice啊...又成功騙自己少寫了好多代碼hhhhh(沒有啦..需求就這樣的嘛...)

順著這樣的思路,我通常寫文都是先在簡書上寫好的,並且簡書有一個特點是所有的圖片,不管是已經發布的文章還是沒有發布的私人文章,都能通過地址取得,可以利用這一點讓簡書當個圖床,誒又少弄了一部分代碼,然後分析分析著就把需求搞成下面這樣了:

技術分享圖片

1)博文管理:

這個比較常規,就不說了;

2)網站數據統計:

作為網站的擁有者和設計者,我當然希望能希望知道這些數據了,然後單獨作為擁有者來說,最好再分為日訪問量/月訪問量/總訪問量這樣子顯示出來,再搞搞樣式,簡直不要太爽;

3)緩存管理:

圖片就沒緩存了,因為保存文章內容我需要保存md源碼,所以可能需要在Redis裏緩存最近常訪問的文章的md轉HTML後渲染好的HTML源碼;

4)系統設置:

網站標題可以改呀,然後導航欄的名字也可以弄弄呀,其實這個也可以不用去搞,只是以防有時候心情不好給一整搗鼓可能心情就好了,hhhhh....;

5)留言管理:

有一些流氓留言可以刪掉,最近學習到的比較好的方法是讓該條數據的狀態為置為0,而不是直接刪除該條數據,這個設計數據庫的時候就需要多設計一個字段,也可以通過用戶留下的Email地址進行回復,最好搞一個自動通知,完美;


表結構設計

通過需求分析,然後嚴格按照《阿裏巴巴Java開發手冊》(下面所說的規範均來自於此)反復分析了很多遍,最終確定了如下的幾張表:

技術分享圖片

然後來具體說一下各個表:

1)日誌表(sys_log):

CREATE TABLE `sys_log` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT COMMENT ‘主鍵‘,
  `ip` varchar(20) NOT NULL DEFAULT ‘‘ COMMENT ‘操作地址的IP‘,
  `create_by` datetime NOT NULL COMMENT ‘操作時間‘,
  `remark` varchar(255) NOT NULL DEFAULT ‘‘ COMMENT ‘操作內容‘,
  `operate_url` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘操作的訪問地址‘,
  `operate_by` varchar(20) DEFAULT ‘‘ COMMENT ‘操作的瀏覽器‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這張表就是拿來保存日誌,用來記錄每一個用戶訪問了哪些地址,使用了什麽樣的瀏覽器,操作內容可以作為一個保留字段,如果以後想要監控用戶行為,那也是可以的~

這裏首先遵守的規範是(下面雷同則不再重復贅述):

  • 第五章第一節第2條(強制)——表名、字段名必須使用小寫字母或數字,禁止出現數字開頭,禁止兩個下劃線中間只出現數字。數據庫字段名的修改代價很大,因為無法進行預發布,所以字段名稱需要慎重考慮;
  • 第五章第一節第3條(強制)——表名不使用復數名詞;
  • 第五章第一節第10條(推薦)——表的命名最好加上“業務名稱_表的作用”

想要拿出來跟大家討論的一則規範是:

  • 第五章第9條(強制)——表必備三個字段:id(unsigned bigint自增),gmt_create(date_time),gmt_modified(date_time)

像如上設計的日誌表,它插入進去了就不會再更新了,而且對於我這個系統也很大概率不會有趣操作這個表的可能,那麽對於這樣不會更新和操作的表,gmt_modified這個字段還有必要存在嗎?

emmm..事實上我問了孤盡大大本人,他回答的簡潔有力:“要的,以備不時之需;”然而原諒我還是沒有聽話,hhhhh,另外一點我想說的是,我忘了是在哪裏看到的了,但是像gmt_create這樣的字段最好設計成create_by這樣,字段本身就是很好的註釋,摁,就喜歡這樣滿滿的細節...

2)瀏覽量表(sys_view):

CREATE TABLE `sys_view` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT,
  `ip` varchar(20) NOT NULL COMMENT ‘訪問IP‘,
  `create_by` datetime NOT NULL COMMENT ‘訪問時間‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這張表用於保存每一次訪問主頁的記錄,我想的是每訪問主頁就記錄增加一條數據,簡單同時也增加訪問量嘛,hhhhh,也是不會更新的一張表,所以沒modifield_by字段;

3)留言/評論表(tbl_message)

CREATE TABLE `tbl_message` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT COMMENT ‘主鍵‘,
  `content` varchar(200) NOT NULL DEFAULT ‘‘ COMMENT ‘留言/評論內容‘,
  `create_by` datetime NOT NULL COMMENT ‘創建日期‘,
  `email` varchar(20) NOT NULL DEFAULT ‘‘ COMMENT ‘郵箱,用於回復消息‘,
  `name` varchar(20) NOT NULL DEFAULT ‘‘ COMMENT ‘用戶自己定義的名稱‘,
  `ip` varchar(20) NOT NULL DEFAULT ‘‘ COMMENT ‘留言/評論IP‘,
  `is_effective` tinyint(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘是否有效,默認為1為有效,0為無效‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘因為message分為兩種,一種是留言,一種是評論,這裏搞成一張表是因為它們幾乎是擁有相同的字段,我覺得沒必要分成兩張表來進行維護‘;

這是評論/留言表,因為考慮到留言和評論有幾乎相同的字段,所以給弄成了一張表,這張表同樣的不需要更新沒有modifield_by字段,這裏遵守的規範是:

  • 第五章第一節第1條(強制)——表達是與否概念的字段,必須使用 is_xxx 的方式命名,數據類型是 unsigned tinyint(1表示是,0表示否)
  • 第五章第一節第15條(參考)——設置合適的字段存儲長度,不但可以節約數據庫表控件和索引存儲,更重要的事能夠提升檢索速度;

4)分類信息表(tbl_sort_info):

CREATE TABLE `tbl_sort_info` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL COMMENT ‘分類名稱‘,
  `number` tinyint(10) NOT NULL DEFAULT ‘0‘ COMMENT ‘該分類下的文章數量‘,
  `create_by` datetime NOT NULL COMMENT ‘分類創建時間‘,
  `modified_by` datetime NOT NULL COMMENT ‘分類修改時間‘,
  `is_effective` tinyint(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘是否有效,默認為1有效,為0無效‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這張表是文章的分類,一開始都忘記設計了....

5)文章信息表(tbl_article_info):

CREATE TABLE `tbl_article_info` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT COMMENT ‘主鍵‘,
  `title` varchar(50) NOT NULL DEFAULT ‘‘ COMMENT ‘文章標題‘,
  `summary` varchar(300) NOT NULL DEFAULT ‘‘ COMMENT ‘文章簡介,默認100個漢字以內‘,
  `is_top` tinyint(1) NOT NULL DEFAULT ‘0‘ COMMENT ‘文章是否置頂,0為否,1為是‘,
  `traffic` int(10) NOT NULL DEFAULT ‘0‘ COMMENT ‘文章訪問量‘,
  `create_by` datetime NOT NULL COMMENT ‘創建時間‘,
  `modified_by` datetime NOT NULL COMMENT ‘修改日期‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這是文章信息表,都是一些基礎常用的字段就不再多做解釋了

6)文章內容表(tbl_article_content):

CREATE TABLE `tbl_article_content` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT,
  `content` text NOT NULL,
  `article_id` bigint(40) NOT NULL COMMENT ‘對應文章ID‘,
  `create_by` datetime NOT NULL COMMENT ‘創建時間‘,
  `modifield_by` datetime NOT NULL COMMENT ‘更新時間‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這是文章內容表,我們並沒有直接把內容字段設計在文章信息表裏,而是單獨建了一個表用來保存文章的內容,然後使用主鍵來關聯,我們這裏遵守的規範是:

  • 第五章第一節第8條(強制)——varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000個字符。如果存儲長度大於此值,則應定義字段類型為text,獨立出來一張表,用主鍵來對應,避免影響其他字段的索引效率;
  • 第五章第三節第6條(強制)——不得使用外鍵與級聯,一切外鍵概念必須在應用層解決;

我試過我現在最長的一篇文章長度大概能存儲8W長度的varchar,所以我就給單獨建一個表分離出來了,使用text類型來保存文章的md源碼

7)文章評論表(tbl_article_message):

CREATE TABLE `tbl_article_message` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT,
  `article_id` bigint(40) NOT NULL COMMENT ‘文章ID‘,
  `message_id` bigint(40) NOT NULL COMMENT ‘對應的留言ID‘,
  `create_by` datetime NOT NULL COMMENT ‘創建時間‘,
  `is_effective` tinyint(1) NOT NULL DEFAULT ‘1‘ COMMENT ‘是否有效,默認為1有效,置0無效‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這其實是一個關聯表,關聯了文章和tbl_message表,用於專門存儲某個文章下的評論信息

8)文章分類表(tbl_article_sort):

CREATE TABLE `tbl_article_sort` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT,
  `sort_id` bigint(40) NOT NULL COMMENT ‘分類id‘,
  `article_id` bigint(40) NOT NULL COMMENT ‘文章id‘,
  `create_by` datetime NOT NULL COMMENT ‘創建時間‘,
  `modified_by` datetime NOT NULL COMMENT ‘更新時間‘,
  `is_effective` tinyint(1) DEFAULT ‘1‘ COMMENT ‘表示當前數據是否有效,默認為1有效,0則無效‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

同樣是一張關聯表,連接了文章和分類,並且同一篇文章能屬於多個分類;

9)文章題圖表(tbl_article_picture):

CREATE TABLE `tbl_article_picture` (
  `id` bigint(40) NOT NULL AUTO_INCREMENT,
  `article_id` bigint(40) NOT NULL COMMENT ‘對應文章id‘,
  `picture_url` varchar(100) NOT NULL DEFAULT ‘‘ COMMENT ‘圖片url‘,
  `create_by` datetime NOT NULL COMMENT ‘創建時間‘,
  `modified_by` datetime NOT NULL COMMENT ‘更新時間‘,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘這張表用來保存題圖url,每一篇文章都應該有題圖‘;

這個是保存每一篇文章的題圖,每一篇文章都因該有題圖;


原型設計

事實上,我是直接先去找的原型,去參考了一下大概我需要做成什麽樣子...

前端原型參考

在這裏先給大家推薦一個設計網站吧,找素材啊之類的還挺方便的:

站酷:http://www.zcool.com.cn/

所以我在裏面找到了我想要的前端原型,大概就像這個樣子:

1)首頁:

技術分享圖片

2)博客頁:

技術分享圖片

3)博文詳情頁:

技術分享圖片

4)博文列表頁:

技術分享圖片

不能再酷了..

後端原型參考

技術分享圖片

emmmm...大概就像這樣了吧,具體的樣式可以到時候再調...

總體是酷的就行!


項目搭建

先來介紹一下這次想要使用的一些技術:

  • SpringBoot / Spring 來編寫後臺
  • Vue 來寫頁面,準備拋棄一下JSP,雖然現在Vue還啥都不懂,學唄
  • MyBatis 用於ORM,喜歡這玩意兒的逆向工程
  • RESTful API / JSON 交互
  • Redis 可能還會使用這個來緩存一下md轉換之後的html源碼

SpringBoot 工程搭建

SpringBoot 項目搭建過程就不再贅述了,不熟悉的童鞋戳這邊:https://www.jianshu.com/p/70963ab49f8c,這裏就簡單給一下配置信息:

技術分享圖片

技術分享圖片

後臺肯定是需要加安全驗證的,要簡單點我可以搞一個攔截器來簡單弄弄,也可以用現有的安全框架,這裏暫時就不加入這方面的東西了,把基本的弄進來就OK,然後它默認加入的東西不能夠支持我們的業務,所以還需要手動添加進一些包:

<?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>cn.wmyskxz</groupId>
    <artifactId>blog</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

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

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.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-web</artifactId>
        </dependency>

        <!--MyBatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--MyBatis逆向工程-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.6</version>
        </dependency>

        <!--SpringBoot測試支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--MySQL-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--SpringBoot熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional> <!-- 這個需要為 true 熱部署才有效 -->
        </dependency>

    </dependencies>

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


</project>

熱部署還是要的呀,然後再在【resrouces】下新建一個【banner.txt】文件,修改一下SpringBoot啟動的提示信息:

 __      __                                 __                        
/\ \  __/\ \                               /\ \                       
\ \ \/\ \ \ \    ___ ___    __  __     ____\ \ \/‘\    __  _  ____    
 \ \ \ \ \ \ \ /‘ __` __`\ /\ \/\ \   /‘,__\\ \ , <   /\ \/‘\/\_ ,`\  
  \ \ \_/ \_\ \/\ \/\ \/\ \\ \ \_\ \ /\__, `\\ \ \\`\ \/>  </\/_/  /_ 
   \ `\___x___/\ \_\ \_\ \_\\/`____ \\/\____/ \ \_\ \_\/\_/\_\ /\____    ‘\/__//__/  \/_/\/_/\/_/ `/___/> \\/___/   \/_/\/_/\//\/_/ \/____/
                                /\___/                                
                                \/__/                                 

弄弄結構,最後整個項目的目錄看起來大概是這個樣子:

技術分享圖片

下面對這些目錄進行一些簡要的說明:

  • controller:控制器
  • dao:實際上這個包可以改名叫mapper,因為裏面放的應該是MyBatis逆向工程自動生成之後的mapper類,還是叫dao吧,傳統...
  • entity:實體類,還會有一些MyBatis生成的example
  • generator:MyBatis逆向工程生成類
  • interceptor:SpringBoot 攔截器
  • service:Service層,裏面還有一層impl目錄
  • util:一些工具類可以放在裏面
  • mapper:用於存放MyBatis逆向工程生成的.xml映射文件
  • static:這個目錄存放一些靜態文件,簡單了解了一下Vue的前後端分離,前臺文件以後也需要放在這個目錄下面

然後我使用application.yml文件代替了application.properties,這個東西結構清晰一點兒,反正用哪個都無所謂,配置好就OK了:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/blog?characterEncoding=UTF-8
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    #Druid連接池配置相關
    druid:
      # 初始大小,最大,最小
      initial-size: 5
      min-idle: 5
      max-active: 20
      # 配置獲取連接等待超時的時間
      max-wait: 60000
      # 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一個連接在池中最小生存的時間,單位是毫秒
      min-evictable-idle-time-millis: 300000

不需要檢測數據庫,不要整這麽復雜,不過倒是需要給數據庫密碼加個密,明文的配置實在不安全,但是現在先不搞了;

MyBatis 逆向工程

使用過MyBatis逆向工程的朋友都應該知道,這東西有個BUG,就是重復生成的時候它並不會覆蓋掉原來的內容(特指xml映射文件),而是會在後面重新生成一遍,這有點兒頭疼,所以首先需要解決這個問題:

首先在【util】包下新建一個【OverIsMergeablePlugin】工具類:

package cn.wmyskxz.blog.util;

import org.mybatis.generator.api.GeneratedXmlFile;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;

import java.lang.reflect.Field;
import java.util.List;

/**
 * 避免MyBatiis重復生成的工具類
 *
 * @author:wmyskxz
 * @create:2018-06-14-上午 9:50
 */
public class OverIsMergeablePlugin extends PluginAdapter {
    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        try {
            Field field = sqlMap.getClass().getDeclaredField("isMergeable");
            field.setAccessible(true);
            field.setBoolean(sqlMap, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}

然後在【generatorConfig.xml】中配置上該工具類:

<plugin type="cn.wmyskxz.blog.util.OverIsMergeablePlugin"/>

好的這樣就搞定了,我們就正式開始我們的逆向工程:

1)編寫generatorConfig.xml逆向工程配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!--避免生成重復代碼的插件-->
        <plugin type="cn.wmyskxz.blog.util.OverIsMergeablePlugin"/>

        <!--是否在代碼中顯示註釋-->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--數據庫鏈接地址賬號密碼-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/blog?characterEncoding=UTF-8" userId="root"
                        password="123456">
        </jdbcConnection>

        <!--生成pojo類存放位置-->
        <javaModelGenerator targetPackage="cn.wmyskxz.blog.entity" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!--生成xml映射文件存放位置-->
        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!--生成mapper類存放位置-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="cn.wmyskxz.blog.dao" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!--生成對應表及類名-->
        <table tableName="sys_log" domainObjectName="SysLog" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <!--使用自增長鍵-->
            <property name="my.isgen.usekeys" value="true"/>
            <!--使用數據庫中實際的字段名作為生成的實體類的屬性-->
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="sys_view" domainObjectName="SysView" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="tbl_article_content" domainObjectName="ArticleContent" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="tbl_article_info" domainObjectName="ArticleInfo" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="tbl_article_message" domainObjectName="ArticleMessage" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="tbl_message" domainObjectName="Message" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="tbl_sort_info" domainObjectName="SortInfo" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="tbl_article_sort" domainObjectName="ArticleSort" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>

    </context>
</generatorConfiguration>

註意表名/生成目標目錄之類的有沒有寫錯,表名最好就直接去復制數據庫中的名稱;

2)編寫逆向工程生成類:

package cn.wmyskxz.blog.generator;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * MyBatis逆向工程生成類
 *
 * @author:wmyskxz
 * @create:2018-06-14-上午 10:10
 */
public class MybatisGenerator {

    public static void main(String[] args) throws Exception {
        String today = "2018-6-14";

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date now = sdf.parse(today);
        Date d = new Date();

        if (d.getTime() > now.getTime() + 1000 * 60 * 60 * 24) {
            System.err.println("——————未成成功運行——————");
            System.err.println("——————未成成功運行——————");
            System.err.println("本程序具有破壞作用,應該只運行一次,如果必須要再運行,需要修改today變量為今天,如:" + sdf.format(new Date()));
            return;
        }

        if (false)
            return;
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        InputStream is = MybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml").openStream();
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(is);
        is.close();
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);

        System.out.println("生成代碼成功,只能執行一次,以後執行會覆蓋掉mapper,pojo,xml 等文件上做的修改");
    }
}

這個是參考自how2j.cn的逆向工程,這個可以說是很成熟的模塊了,寫的很棒,考慮了安全方面的一些東西,鏈接在這裏:http://how2j.cn/k/tmall_ssm/tmall_ssm-1547/1547.html

3)點擊運行:

控制臺看到成功的信息之後,就能看到項目中自動多了一堆文件了:

技術分享圖片

技術分享圖片

RESTful API 設計

為了實現前後端分離,好的RESTful API是離不開的,正好前一段時間學習了這方面的知識,所以決定先來設計一套RESTful API,之前學習的文章鏈接在這裏:https://www.jianshu.com/p/91600da4df95

1)引入Swagger2來構造RESTful API:

既然想弄一下前後端分離,那就徹底一點兒,寫後臺完全不管前臺,前後臺的交互靠一套RESTful API和JSON數據來弄,所以需要一個文檔來瞅瞅,首先在pox.xml添加相關依賴:

<!--Swagger2支持-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.2.2</version>
</dependency>

2)創建Swagger2配置類:

在SpringBoot啟動類的同級目錄下創建Swagger2的配置類【Swagger2】:

/**
 * Swagger2 配置類
 *
 * @author:wmyskxz
 * @create:2018-06-14-上午 10:40
 */
@Configuration
@EnableSwagger2
public class Swagger2 {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("cn.wmyskxz.blog"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Wmyskxz個人博客RESTful APIs")
                .description("原文地址鏈接:http://blog.didispace.com/springbootswagger2/")
                .termsOfServiceUrl("http://blog.didispace.com/")
                .contact("@我沒有三顆心臟")
                .version("1.0")
                .build();
    }

}

這樣,就可以在我們啟動項目之後,訪問http://localhost:8080/swagger-ui.html地址來查看當前項目中的RESTful風格的API:

技術分享圖片

3)設計RESTful API:

好的,搗鼓了半天,終於有了一些雛形:

技術分享圖片

但是這也只是設計了API,具體都還沒有實現,這些就在寫後臺的時候來完善了,具體的這些內容怎麽顯示出來的,我給一個【SortController】的參考類:

/**
 * 分類信息控制器
 *
 * @author:wmyskxz
 * @create:2018-06-14-下午 13:25
 */
@RestController
@RequestMapping("/api/sort")
public class SortController {

    /**
     * 獲取所有分類信息
     *
     * @return
     */
    @ApiOperation("獲取所有分類信息")
    @GetMapping("/list")
    public List<SortInfo> listAllSortInfo() {
        return null;
    }

    /**
     * 通過id獲取一條分類信息
     *
     * @param id
     * @return
     */
    @ApiOperation("獲取某一條分類信息")
    @ApiImplicitParam(name = "id", value = "分類ID", required = true, dataType = "Long")
    @GetMapping("/{id}")
    public SortInfo getSortInfoById(@PathVariable Long id) {
        return null;
    }

    /**
     * 增加一條分類信息數據
     *
     * @return
     */
    @ApiOperation("增加分類信息")
    @ApiImplicitParam(name = "name", value = "分類名稱", required = true, dataType = "String")
    @PostMapping("")
    public String addSortInfo() {
        return null;
    }

    /**
     * 更新/編輯一條數據
     *
     * @param id
     * @return
     */
    @ApiOperation("更新/編輯分類信息")
    @ApiImplicitParam(name = "id", value = "分類ID", required = true, dataType = "Long")
    @PutMapping("/{id}")
    public String updateSortInfo(@PathVariable Long id) {
        return null;
    }

    /**
     * 根據ID刪除分類信息
     *
     * @param id
     * @return
     */
    @ApiOperation("刪除分類信息")
    @ApiImplicitParam(name = "id", value = "分類ID", required = true, dataType = "Long")
    @DeleteMapping("/{id}")
    public String deleteSortInfo(@PathVariable Long id) {
        return null;
    }
}

簡單介紹一下這些Swagger2的註解吧:

  • @ApiOperation:用於給API設置提示信息,就上圖中右邊顯示的那些,默認不寫的情況下是value屬性,還可以多寫一個notes屬性,用於詳細的描述API,這裏就不需要了,都還比較簡單;
  • @ApiImplicaitParam:用於說明API的參數信息,加了s的註解同理,寫了這個之後呢,我們就可以利用Swagger2給我們的信息頁面進行測試了,當然這裏沒有具體實現,也可以來看一下(下圖);

技術分享圖片

這裏沒有具體實現所以就不足以完成測試,等到後臺編寫的時候再進行測試吧...


總結

至此呢,我們項目所需要的準備就差不多完成了,想要去做一個東西必須要清楚的知道要的是一個什麽東西,這樣才能更加好的完成我們的產品,這也是我喜歡和堅信的事情:方向永遠比努力重要!(強行有聯系..hhhh)

另外一個問題: 我在想文章信息和內容分成了兩個表的問題,這樣的設計我覺得是沒有問題的,但是作為前端並不關心這些數據庫的設計,他只要能拿到對象就可以了,在設計 API 的時候,就發現獲得一篇文章,需要從三個表(文章信息/文章內容/評論)去獲取信息並封裝返回前端,這就需要自己在後臺另外寫一個實體類去封裝這些信息,這無疑增加了我們的代碼工作量,有沒有什麽好的方法解決呢?

歡迎轉載,轉載請註明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關註公眾微信號:wmyskxz_javaweb
分享自己的Java Web學習之路以及各種Java學習資料

SpringBoot技術棧搭建個人博客【項目準備】