【Spring Boot】搭建個人博客 - 后端環(huán)境搭建

我不能停滯不前

前言

諸君,搭建個人博客系列缭受,我終于開始更了胁澳。。沒想到還有人催更米者,受寵若驚韭畸!
在閱讀本文前,我假設(shè)您除了Spring Boot蔓搞,還對以下技術(shù)有了解

  • ORM框架:Mybatis-Plus
  • 插件工具:Lomok
  • 數(shù)據(jù)庫連接池:Druid
  • 模板引擎:Freemark胰丁,Thymeleaf
  • API文檔框架:Swagger

為了用戶體驗,正文部分是一氣呵成的喂分,搭建過程中所遇問題隘马,可以看踩坑紀部分。
提醒一下:
若是您要參考正文部分來搭建環(huán)境妻顶,請務(wù)必看踩坑紀部分酸员,否則只看正文部分葱她,不便于理解且可能項目會跑不起來偷线。


好吧。游岳。其實這里本來還有一些話的沥潭,但是發(fā)現(xiàn)寫的太長了邀泉。。也是為了不影響讀者體驗钝鸽,放在了后記中汇恤;畢竟各位是來看技術(shù)文章的,不是看我瞎BB的拔恰,不能主次顛倒因谎。
開始正文

正文

1. 數(shù)據(jù)庫設(shè)計

數(shù)據(jù)庫設(shè)計是最初的階段,同時我也是卡在這階段最久颜懊。至于為什么卡這么久請看踩坑紀部分
根據(jù)我上一篇的【Spring Boot】搭建個人博客 - 需求分析設(shè)計數(shù)據(jù)庫表财岔。
數(shù)據(jù)庫設(shè)計規(guī)范主要參考阿里巴巴Java開發(fā)手冊 1.4.0风皿,以及一些網(wǎng)上資料;
表名前綴是模塊名匠璧,分了8個模塊桐款。
一共18張表,其中16張實體表夷恍,2張多對多關(guān)系表魔眨;
sql文件會與項目一同上傳至Github

數(shù)據(jù)庫表結(jié)構(gòu)

2. idea創(chuàng)建Spring boot項目

具體創(chuàng)建過程參考這里:【Spring Boot】初體驗
給出application.properties和pom.xml


application.properties

#上下文
server.servlet.context-path=/blog

#日志配置
logging.level.org.springframework.web=DEBUG

#Thymeleaf 配置
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
##緩存設(shè)置為false, 這樣修改之后馬上生效,便于調(diào)試酿雪;生產(chǎn)環(huán)境下可以設(shè)置為true
spring.thymeleaf.cache=false

#Druid配置
##JDBC配置
spring.datasource.druid.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&useSSL=false&characterEncoding=utf8
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.username=root
spring.datasource.druid.password=root
##連接池配置
spring.datasource.druid.max-active=20


#Freemarker配置
##不使用文件系統(tǒng)優(yōu)先冰沙,而使用classpath下的資源文件優(yōu)先,解決打jar包運行后执虹,出現(xiàn)的異常問題
spring.freemarker.prefer-file-system-access=false

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>top.sinch</groupId>
    <artifactId>blog</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--Spring Boot 熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--MyBatis Plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>

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

        <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

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

        <!--Lomok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>

        <!--Gson-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

        <!--Freemarker模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <!--Thymeleaf模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-->Thymeleaf的非嚴格HTML格式包-->
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>

    </dependencies>

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


</project>

3. Mybatis-Plus代碼生成器生成模板代碼

使用Mybatis-Plus官方提供的代碼生成器Demo生成模板代碼拓挥。
需要注意的是:

  • 生成器根據(jù)數(shù)據(jù)庫表名,一次生成的是同一模塊下代碼袋励;若有幾個模塊則需要更改模塊名及相對應(yīng)表名侥啤;有幾個模塊,執(zhí)行幾次生成器生成對應(yīng)模塊下的代碼
  • 若只有一個模塊或者不區(qū)分模塊茬故,則只用執(zhí)行一次生成器

新建包路徑
src\main\java下新建包路徑com.baomidou.mybatisplus盖灸,把代碼生成器和項目代碼區(qū)分開

項目結(jié)構(gòu)

代碼生成器(把官方Demo根據(jù)需求改造了一下)

package com.baomidou.mybatisplus;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;

/**
 * mybatis-plus 代碼生成器
 *
 * @Author sincH
 * @Date 2018/11/10
 */
public class CodeGenerator {
    public static void main(String[] args) {
        // 代碼生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        //項目根目錄
        String projectPath = "D:/_Code/idea/blog";
//        String projectPath = "C:/Users/sincH/Desktop/Temp/blog";
        //Java源碼輸出目錄
        gc.setOutputDir(projectPath + "/src/main/java");
        //作者
        gc.setAuthor("sincH");
        //是否打開輸出目錄,默認true
        gc.setOpen(false);
        //開啟swagger2模式(將實體類默認的Javadoc文檔注解轉(zhuǎn)為Swagger文檔注解)磺芭,默認false
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        // 數(shù)據(jù)源配置
        DataSourceConfig dsc = new DataSourceConfig();
        //數(shù)據(jù)庫連接URL
        dsc.setUrl("jdbc:mysql://localhost:3306/blog?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");//數(shù)據(jù)庫模式赁炎;MySQL中庫即模式
        //數(shù)據(jù)庫驅(qū)動
        dsc.setDriverName("com.mysql.jdbc.Driver");
        //數(shù)據(jù)庫用戶名
        dsc.setUsername("root");
        //數(shù)據(jù)庫密碼
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //模塊名
        pc.setModuleName("article");
        //父包名;模塊將在父包下生成
        pc.setParent("top.sinch.blog");
        mpg.setPackageInfo(pc);

        // 自定義配置
//        InjectionConfig cfg = new InjectionConfig() {
//            @Override
//            public void initMap() {
//                // to do nothing
//            }
//        };
//        List<FileOutConfig> focList = new ArrayList<>();
//        focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
//            @Override
//            public String outputFile(TableInfo tableInfo) {
//                // 自定義輸入文件名稱
//                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
//                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
//            }
//        });
//        cfg.setFileOutConfigList(focList);
//        mpg.setCfg(cfg);
        //setXml(null)代表不生成xml文件
        mpg.setTemplate(new TemplateConfig().setXml(null));

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //表名下劃線轉(zhuǎn)駝峰
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //列名下劃線轉(zhuǎn)駝峰
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //Boolean類型字段是否移除is前綴(默認 false)
        strategy.setEntityBooleanColumnRemoveIsPrefix(true);
//        strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
        //是否開啟實體類Lombok模式(默認false)
        strategy.setEntityLombokModel(true);
        //是否開啟RestController風(fēng)格
        strategy.setRestControllerStyle(true);
//        strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
        //表名(數(shù)據(jù)庫中存在的表);多表傳數(shù)組
        strategy.setInclude(new String[]{
                "article_info","article_content","article_comment","article_comment_reply",
                "article_archive","article_label","article_category",
                "r_article_info_article_label","r_article_info_article_category"
        });
//        strategy.setSuperEntityColumns("id");
        //駝峰轉(zhuǎn)連字符(是否開啟Controller映射連字符風(fēng)格)
        strategy.setControllerMappingHyphenStyle(true);

        /**
         *設(shè)置表名前綴钾腺;
         *此表名前綴若與數(shù)據(jù)庫表名前綴一致徙垫,則自動創(chuàng)建實體類時,實體類名匹配數(shù)據(jù)庫表名下劃線后面的名放棒;
         *栗子:數(shù)據(jù)庫表名為 admin_info 姻报,當(dāng)strategy.setTablePrefix("admin_")時,則實體類名為Info间螟;
         *若不設(shè)置表名前綴吴旋,則實體類名匹配數(shù)據(jù)庫表名
         */
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);

        //設(shè)置代碼生成器的模板引擎
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        //執(zhí)行代碼生成器
        mpg.execute();
    }
}


需要注意的是:
把代碼生成器生成的代碼輸出目錄設(shè)置為項目地址,這樣就可以不用在代碼生成后還要手動復(fù)制到項目中厢破。

輸出目錄與項目地址對應(yīng)


生成器生成結(jié)果及其對應(yīng)模塊
這里是9個模塊荣瑟,多了一個core(核心)模塊;是放項目的配置類摩泪,攔截器笆焰,過濾器等等,其實也可以不用建加勤;不建的話仙辟,配置類那些比較散亂分布,我覺得還是集中比較好鳄梅,就建了個core包

需求對應(yīng)模塊



修改生成器生成的模板代碼命名
有時候叠国,模板代碼的命名不太符合需求,比如我這里statistics(統(tǒng)計)模塊下有個統(tǒng)計IP的子模塊戴尸,模板代碼用了駝峰命名把IP命名為Ip粟焊,我覺得還是都用大寫字母表示IP好

statistics模塊

4. 單元測試

基于以上,若是單模塊項目或者多模塊項目無同名Class的話孙蒙,項目的后端環(huán)境搭建就基本到此完成了项棠,寫個單元測試;不會寫單元測試的請參考嘟嘟獨立博客的這篇文章:Spring Boot干貨系列:(十二)Spring Boot使用單元測試挎峦。

新建包路徑
test文件下新建要進行單元測試的類的相對應(yīng)包路徑香追;
這里舉例,要進行單元測試的類是ContentMapper

ContentMapper類單元測試

ContentMapper單元測試

package top.sinch.blog.article.mapper;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import top.sinch.blog.BlogApplication;
import top.sinch.blog.article.entity.Content;

import javax.annotation.Resource;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class ContentMapperTest {
//    @Autowired(required = false)//允許依賴對象為null
    @Resource
    private ContentMapper contentMapper;

    @Test
    public void insert() {
        Content content = new Content();
        content.setDetail("23333333333333333333333333333333");
//        content.setArticleInfoId(Integer.toUnsignedLong(1));
        int result = contentMapper.insert(content);
        Assert.assertThat(result, equalTo(1 ));
    }

}

單元測試結(jié)果
test passed:測試通過

單元測試結(jié)果

但是請注意坦胶,我這里強調(diào)的是單模塊項目或者多模塊項目無同名Class透典,而我的這個個人博客項目是多模塊且不同模塊下有同名Class的,所以您會發(fā)現(xiàn)單元測試會失敗顿苇,報以下錯誤或者類似錯誤

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [top.sinch.blog.BlogApplication]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'infoController' for bean class [top.sinch.blog.admin.controller.InfoController] conflicts with existing, non-compatible bean definition of same name and class [top.sinch.blog.about.controller.InfoController]

這個坑我就不放在踩坑紀部分說了峭咒,在這里解釋;
出現(xiàn)這個錯誤的原因:
是因為本項目中不同模塊下都有一個Info實體類纪岁,相對應(yīng)的就都有InfoController凑队,InfoServiceImpl,InfoMapper幔翰;若不為不同模塊下的InfoController漩氨,InfoServiceImpl,InfoMapper取別名的話遗增,這些InfoController等在注冊進Spring容器時就會失敳挪ぁ;因為Spring默認是用類名進行Bean注冊的贡定,而它們類名又都是InfoController等赋访,自然會失敗


這里舉例about模塊和admin模塊,以便于理解

about模塊和admin模塊

about模塊和admin模塊的Controller

解決方法
為不同模塊下的InfoController缓待,InfoServiceImpl蚓耽,InfoMapper取別名;
同樣舉例旋炒,about模塊和admin模塊步悠。

為不同模塊下InfoController取別名

然后再進行單元測試,完美通過瘫镇!Perfect6κ蕖答姥!

5. 設(shè)計RESTful API

初次嘗試在項目中設(shè)計RESTful API;這里鑒于篇幅原因谚咬,僅給出一個Demo鹦付,其余都是類似的;不過在Demo之前先講講RESTful API設(shè)計規(guī)范择卦,規(guī)范主要參考阮一峰的這篇RESTful API 最佳實踐


RESTful API 設(shè)計規(guī)范
阮一峰RESTful API 最佳實踐推薦使用復(fù)數(shù)URL敲长,但是我根據(jù)項目情況并沒有采用復(fù)數(shù)URL;
這里以article模塊下的InfoController為例子

  • GET /article/info/{id} 獲取單個文章信息
  • GET /article/info/all 獲取所有文章信息
  • POST /article/info 新增一篇文章信息
  • PUT /article/info 更新一篇文章信息
  • DELETE /article/info/{id} 刪除一篇文章信息

swagger2配置類

package top.sinch.blog.core.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Swagger2配置類
 *
 * @Author sincH
 * @Date 2018/11/14
 */
@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("top.sinch.blog"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("sincH個人博客RESTful APIs")
                .description("sincH個人博客API文檔")
                .contact(new Contact("sincH","http://www.reibang.com/u/64e4e9db42c9","923395273@qq.com"))
                .version("1.0")
                .build();
    }
}


article模塊下的InfoController

package top.sinch.blog.article.controller;


import com.google.gson.Gson;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import top.sinch.blog.article.entity.Info;

/**
 * <p>
 *  前端控制器  文章信息
 * </p>
 *
 * @author sincH
 * @since 2018-11-20
 */
@RestController("articleInfoController")
@RequestMapping("/article/info")
public class InfoController {
    @ApiOperation("獲取單個文章信息")
    @GetMapping("/{id}")
    public String get(@PathVariable("id") String id) {
        Info articleInfo = new Info();
        articleInfo.setAuthor("sincH");
        articleInfo.setTitle("Test");
        return new Gson().toJson(articleInfo);
    }

    @ApiOperation("獲取所有文章信息")
    @GetMapping("/all")
    public String list(){
        return "list successful";
    }

    @ApiOperation("新增一篇文章信息")
    @PostMapping("")
    public String save(){
        return "save successful";
    }

    @ApiOperation("更新一篇文章信息")
    @PutMapping("")
    public String update(){
        return "update successful";
    }

    @ApiOperation("刪除一篇文章信息")
    @DeleteMapping("/{id}")
    public String remove(@PathVariable("id") String id){
        return "remove successful";
    }
}

除了以上步驟外秉继,肯定要先導(dǎo)入swagger相關(guān)依賴包祈噪;
有認真看的看官,應(yīng)該知道我已經(jīng)先在pom.xml中導(dǎo)入了swagger相關(guān)依賴包
所以設(shè)計完RESTful API尚辑,直接在瀏覽器輸入地址:
http://localhost:8080/blog/swagger-ui.html
若您更改了端口和項目名辑鲤,請相對應(yīng)更改地址;不然肯定訪問不了(感覺說這話有點多余)

swagger

對某個RESTful API進行測試

RESTful API測試

踩坑紀

紀一 :命名規(guī)范問題

命名規(guī)范是我這么久都沒更新文章的主要原因杠茬;我因為命名問題卡在了數(shù)據(jù)庫設(shè)計階段超級久遂填;每天上班前的一小時,下班后的兩小時都在想數(shù)據(jù)庫表名澈蝙,字段名吓坚,類名等怎么命名會對后面的開發(fā)比較友好;
沒錯灯荧!您也不用懷疑礁击;就是命名問題卡了我許久,也不是什么技術(shù)大問題逗载;因為我對命名有些許強迫癥哆窿,命名不好就會推翻重來;不過倘若是別人一開始就設(shè)計好的命名厉斟,我其實也不會太糾結(jié)挚躯。關(guān)鍵這是我自己設(shè)計的就會十分糾結(jié)。
光是最初的數(shù)據(jù)庫表名設(shè)計擦秽,我就有好幾種命名码荔;


以文章信息表為例,有以下命名

  • article
  • article_base
  • article_article_info
  • article_info

命名規(guī)范參考了阿里巴巴Java開發(fā)手冊 1.4.0感挥,以及一些網(wǎng)上資料缩搅;
接下來逐個分析為什么我使用這個命名,以及為什么拋棄這個命名触幼;可能比較長硼瓣,希望您有耐心看;可能有些人覺得沒必要這么糾結(jié)命名置谦,那么您可以跳過這一紀堂鲤,看下一個坑的紀錄亿傅。

  • article

    • 為什么使用這個命名
      article這命名其實不用過多解釋,當(dāng)你想設(shè)計文章表時瘟栖,那么article肯定是你的首選命名葵擎,可能您看到過essay這個命名,當(dāng)然也是可以的慢宗,但是推薦使用article
    • 為什么拋棄這個命名
      根據(jù)阿里巴巴Java開發(fā)手冊 1.4.0中的MySQL數(shù)據(jù)庫(一)建表規(guī)約第8條
    1. 【強制】varchar 是可變長字符串坪蚁,不預(yù)先分配存儲空間奔穿,長度不要超過5000镜沽,如果存儲長度大于此值,定義字段類型為 text贱田,獨立出來一張表缅茉,用主鍵來對應(yīng),避免影響其它字段索引效率男摧。

    那么article(文章)表中肯定有個content(內(nèi)容)字段蔬墩,而content字段長度不確定,有可能會超過5000耗拓,所以我決定將content獨立成表拇颅,命名為article_content;
    那么article乔询,article_content不太統(tǒng)一樟插,要么都是“XXX_XXX”格式,要么就都不是比較好竿刁;而且根據(jù)阿里巴巴Java開發(fā)手冊 1.4.0中的MySQL數(shù)據(jù)庫(一)建表規(guī)約第10條

    1. 【推薦】表的命名最好是加上“業(yè)務(wù)名稱_表的作用”黄锤。 正例:alipay_task / force_project / trade_config

    因此將article重命名為article_base

  • article_base

    • 為什么使用這個命名
      因為拋棄了article命名,所以就改成了article_base食拜;可能有人會說為什么不用article_info呢鸵熟,因為我覺得文章表直接命名article就行了,如果命名article_info有點多此一舉负甸,但是又要分表流强,所以就想到了article_base,直譯為文章基礎(chǔ)表呻待,以后要是想擴展表煮盼,可以命名為article_extra之類的,妙啊
    • 為什么拋棄這個命名
      因為使用MyBatis-Plus的代碼生成器時带污,倘若設(shè)置了表名前綴且表名前綴與數(shù)據(jù)庫表名前綴一致僵控,那么代碼生成器生成的實體類名為表名前綴后的名。
      舉個栗子:
      也就是說倘若代碼生成器里設(shè)置了表名前綴為"article_"鱼冀,而數(shù)據(jù)庫表名為"article_base"报破,則實際由代碼生成器生成的實體類名為Base悠就。
      那么實體類名為Base,不如Info來得更直觀友好充易,因此拋棄article_base命名
       //省略前面的配置

        //模塊名
        pc.setModuleName("article");

       //省略中間的配置

        //表名(數(shù)據(jù)庫中存在的表);多表傳數(shù)組
        strategy.setInclude(new String[]{
                "article_base"
        });

        /**
         *設(shè)置表名前綴梗脾;
         *此表名前綴若與數(shù)據(jù)庫表名前綴一致,則自動創(chuàng)建實體類時盹靴,實體類名匹配數(shù)據(jù)庫表名前綴后面的名炸茧;
         *栗子:數(shù)據(jù)庫表名為 article_base ,當(dāng)strategy.setTablePrefix("article_")時稿静,則實體類名為Base梭冠;
         *若不設(shè)置表名前綴,則實體類名匹配數(shù)據(jù)庫表名
         */
        strategy.setTablePrefix(pc.getModuleName() + "_");

  • article_article_info
    • 為什么使用這個命名
      前面分析article_base這個命名時說到了改备,代碼生成器的表名前綴若與數(shù)據(jù)庫的表名前綴一致時控漠,則生成的實體類名為表名前綴后的名;
      也就是說代碼生成器的表名前綴為"article_"悬钳,數(shù)據(jù)庫表名為"article_info"盐捷,則實際由代碼生成器生成的實體類名為Info。而單單Info這個名并不夠直觀默勾,我希望它是ArticleInfo碉渡,因此article_info前面再加了article,這樣命名就是article_article_info母剥,然后代碼生成器生成的實體類名就是ArticleInfo滞诺,不過這樣看著就很重復(fù)了。
    • 為什么拋棄這個命名
      拋棄的原因很簡單媳搪,因為article_article_info這樣的命名看著很重復(fù)铭段,很不舒服,心里難受秦爆,就拋棄了序愚。。
  • article_info
    • 為什么最終使用這個命名
      使用這個命名的原因也很簡單等限,在拋棄了article爸吮,article_base,article_article_info這幾個命名后望门,權(quán)衡利弊形娇,綜合考慮,還是覺得article_info這個命名好

命名規(guī)范這個坑我寫得還挺長筹误,我覺得能看完命名規(guī)范這個坑的都是勇者桐早,我為你們鼓掌,畢竟看我在命名規(guī)范這里瞎BB也不容易。

紀二:數(shù)據(jù)庫建立索引

在搞定命名規(guī)范問題后哄酝,又要面對索引問題友存。
在進行數(shù)次網(wǎng)上沖浪后,對怎么建立索引進行了簡單總結(jié)陶衅。

  • 【推薦】對出現(xiàn)在SQL語句的WHERE后面的字段建立索引
  • 【推薦】對需要精確查詢("=","IN"和"<=>"查詢)的字段建立HASH索引
  • 【推薦】對需要范圍查詢的字段建立BTREE索引

好屡立!總結(jié)完畢!可能有些人覺得太簡單了搀军,的確膨俐,相對于其他關(guān)于索引的文章,我總結(jié)的是很簡單罩句,所以僅僅只能適用于簡單的情況焚刺,至于什么是簡單的情況,請各位自己思考的止;
關(guān)于我參考的網(wǎng)上沖浪資料在文末的參考文章給出

紀三:模型同步到數(shù)據(jù)庫中出現(xiàn)問題

Navicat Premuim中設(shè)計模型后同步到數(shù)據(jù)庫中出現(xiàn)問題

模型同步到數(shù)據(jù)庫失敗

分析
因為我在某些字段上加了HASH(哈希)索引檩坚,而HASH索引是不支持排序的着撩;而從模型同步到數(shù)據(jù)庫時诅福,建立了索引的字段默認用了ASC排序(升序排列),就算你建立索引時不設(shè)置排序方式拖叙,同步時也會默認用ASC排序氓润,這有點坑。薯鳍。而HASH索引不支持排序咖气,自然同步會失敗。

  • 模型中并沒有設(shè)置排序方式


    排序方式
  • 將模型導(dǎo)出為SQL文件挖滤,發(fā)現(xiàn)默認用了ASC排序


    ASC排序

解決方法
將模型導(dǎo)出為SQL文件崩溪,再將SQL文件中的ASC去除,最后在Navicat Premium中運行SQL文件斩松,完美解決伶唯!

后記

。惧盹。終于乳幸,我!寫到了后記钧椰;實不相瞞粹断,這篇文章,我也寫了好久嫡霞,期間歷經(jīng)修修改改瓶埋,推翻重來之類的。下面就是我瞎BB的時間了;it's my show time!!

  • Q:別人的項目后端環(huán)境都搭建很快的养筒,為什么你用了這么久=坪骸!
    A:你也說是別人了闽颇,我就是我盾戴,是與眾不同的我;好吧兵多,開玩笑的尖啡,主要是兩點原因;
    其一:命名規(guī)范問題剩膘;沒錯衅斩,就是我在踩坑紀中也提到的命名規(guī)范問題。以前我搭建項目后端環(huán)境也是很快的怠褐,好吧畏梆。不過,那時我并沒有考慮太多命名規(guī)范什么的奈懒,導(dǎo)致我后面再看自己的代碼時奠涌,就覺得 這是誰寫的代碼,什么鬼傲仔印溜畅!
    其二:數(shù)據(jù)庫索引;沒錯极祸,同樣是在踩坑紀中也提到的索引問題慈格;呀,怎么去建立索引遥金,我也思考了好久浴捆。
    以上便是我后端環(huán)境搭建這么久的原因。其實并不是什么技術(shù)上的大問題

  • Q:就算是那兩點原因稿械,那也不用久啊选泻,從你項目需求分析開始,到現(xiàn)在兩個多月了都溜哮,黃花菜都涼了滔金。
    A:行的吧,的確也是不用那么久茂嗓。還有原因就是我在實習(xí)餐茵。∈鑫可能你想說這不是理由忿族,好吧锣笨,不是就不是,但我也利用了每天上班前的一小時道批,下班后的兩小時在做這個項目了错英。我在努力了,為什么還是這么慢呢隆豹,究其原因椭岩,我還是知道的,就是目前的我還是太弱雞了璃赡,不夠強啊判哥。嗯,我不能停滯不前碉考。
    再有就是時不時懶癌發(fā)作塌计。。就沒有然后了
    綜上侯谁,弱雞+間歇性懶癌發(fā)作=久

  • Q:拖了這么久锌仅,對甲方爸爸有沒有什么影響?
    A:獨秀同學(xué)墙贱,請你坐下热芹,一看你就沒認真看我的文章,我這是個人博客項目嫩痰,甲方爸爸不是其他人剿吻,就是我自己窍箍,至于對我有沒有影響串纺,請看下一個QA部分。
    如果硬要說甲方爸爸是其他人的話椰棘,那這個其他人就是關(guān)注我這個博客項目的人纺棺;我看到評論區(qū)有人催更的時候,真是有點受寵若驚邪狞。
    當(dāng)然要是有真.甲方爸爸或者是商業(yè)項目的話祷蝌,我肯定不會拖這么久了。要是真拖這么久的話帆卓,你這是在玩火啊巨朦,少年

  • Q:拖了這么久,對你有沒有什么影響剑令?
    A:一開始我以為不會有什么影響糊啡,直到開始寫這篇文章時,才意識到影響太大了吁津。
    消極影響:開始寫這篇文章時棚蓄,肯定要梳理一下思路,然后我就發(fā)現(xiàn)隔了太久了,有些思路就不知道是怎么回事了梭依;有些bug稍算,踩過的坑也因為太久了就忘記了;雖然平時在遇到bug時役拴,會進行速記糊探,但時間久了,就不知道這速記記的是什么鬼河闰。所以影響真的是很大侧到,我以后應(yīng)該不會拖這么久了。
    積極影響:說實話淤击,我自己也沒想到讓我卡殼這么久的并不是什么技術(shù)大問題匠抗,而是命名之類的設(shè)計問題。
    突然想起很久之前污抬,一位已經(jīng)在外工作的師兄說的一句話汞贸,你不能僅僅是Coder,還要是Designer印机;感覺自己已經(jīng)朝著Designer往前了一步

終于矢腻。∩淙快要寫完了多柑,這篇文章寫得真是又長又久,能看到這里的楣责,我真心覺得你牛逼
最后說一句竣灌,因為文章我寫的又長又久,也不想回頭校對秆麸,所以要是文章中有出錯的地方或者您有其他意見初嘹,請在評論區(qū)中賜教

參考文章

因為隔了太久了,而且這次參考的很多沮趣,有些參考文章就忘記收藏了屯烦,下面給出我收藏記的:
阿里巴巴Java開發(fā)手冊 1.4.0
MySQL命名、設(shè)計及使用規(guī)范
數(shù)據(jù)庫設(shè)計中的命名規(guī)范
字段類型與合理的選擇字段類型
MySQL索引類型 btree索引和hash索引的區(qū)別
Mybatis-Plus代碼生成器
Spring Boot干貨系列:(十二)Spring Boot使用單元測試
Spring Boot中使用Swagger2構(gòu)建強大的RESTful API文檔
RESTful API 最佳實踐

項目地址

Github:h-blog

小結(jié)

最后做個小結(jié)吧房铭。驻龟。本來不用做的。拖得太久了缸匪。翁狐。我也很絕望啊
希望自己能吸取這次教訓(xùn)吧,下次不要拖這么久了豪嗽。
再再說一次谴蔑,要是文中有錯誤或者有其他意見豌骏,請私信我或者評論區(qū)留言

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市隐锭,隨后出現(xiàn)的幾起案子窃躲,更是在濱河造成了極大的恐慌,老刑警劉巖钦睡,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒂窒,死亡現(xiàn)場離奇詭異,居然都是意外死亡荞怒,警方通過查閱死者的電腦和手機洒琢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褐桌,“玉大人衰抑,你說我怎么就攤上這事∮叮” “怎么了呛踊?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啦撮。 經(jīng)常有香客問我谭网,道長,這世上最難降的妖魔是什么赃春? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任愉择,我火速辦了婚禮,結(jié)果婚禮上织中,老公的妹妹穿的比我還像新娘锥涕。我一直安慰自己,他們只是感情好抠璃,可當(dāng)我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布站楚。 她就那樣靜靜地躺著,像睡著了一般搏嗡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拉一,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天采盒,我揣著相機與錄音,去河邊找鬼蔚润。 笑死磅氨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嫡纠。 我是一名探鬼主播烦租,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼延赌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叉橱?” 一聲冷哼從身側(cè)響起挫以,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窃祝,沒想到半個月后掐松,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡粪小,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年大磺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片探膊。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡杠愧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逞壁,到底是詐尸還是另有隱情殴蹄,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響敬惦,放射性物質(zhì)發(fā)生泄漏戴甩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一擦盾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦姨丈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至趁冈,卻和暖如春歼争,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渗勘。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工沐绒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人旺坠。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓乔遮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親取刃。 傳聞我的和親對象是個殘疾皇子蹋肮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,446評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,275評論 25 707
  • 用兩張圖告訴你出刷,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,744評論 2 59
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理坯辩,服務(wù)發(fā)現(xiàn)馁龟,斷路器,智...
    卡卡羅2017閱讀 134,695評論 18 139
  • 世有萬千事濒翻,有會不會事屁柏, 有擅不擅事,有經(jīng)未經(jīng)事有送, 有可不可事淌喻,有爭不爭事, 劣態(tài)是它事雀摘,做好應(yīng)做事裸删。
    明哥明說閱讀 131評論 1 1