天天看點

SpringBoot開發記錄1.快速初始化2.pom.xml檔案3.Mybatis自動生成代碼4.查詢樣例5.關于事物6.druid監控7.Controller注:8.Swagger9.關于restful10.關于全局異常捕獲參考文獻

目錄

1.快速初始化

2.pom.xml檔案

3.Mybatis自動生成代碼

3.1 建立generatorCOnfig檔案

3.2 pom.xml檔案中添加插件

3.3 開始生成

3.4 使用

4.查詢樣例

4.1 普通單表操作

4.2 自增主鍵表的操作

4.3 聯合查詢

4.4 分頁查詢

5.關于事物

5.1 關于@EnableTransactionManagement注解

5.2 關于@Transactional注解的位置

5.3 關于try/catch

5.4 關于事物傳播

6.druid監控

7.Controller

8.Swagger

8.1 添加maven依賴

8.2 添加自動配置類

8.3 給接口配置說明

8.4 界面化接口說明文檔

9.關于restful

10.關于全局異常捕獲

參考文獻

1.快速初始化

SpringBoot基礎工程的搭建,可以通過這裡線上界面化配置生成,生成後,直接本地導入即可。

其中,Dependencies中點選See all可以選擇需要一類的jar包,一般情況下,可以選擇以下幾個:

SpringBoot開發記錄1.快速初始化2.pom.xml檔案3.Mybatis自動生成代碼4.查詢樣例5.關于事物6.druid監控7.Controller注:8.Swagger9.關于restful10.關于全局異常捕獲參考文獻

選完之後,點選Generate Project 就生成基礎工程了,下載下傳到本地,導入到idea即可。

2.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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tydic</groupId>
    <artifactId>cloud_wind</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud_wind</name>
    <description>Demo project for Spring Boot</description>
    <!--打包方式,可以配置成jar或者war-->
    <packaging>jar</packaging>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</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>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.29</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
            <!--添加mybatis generator maven插件,自動生産mybatis對應的實體類-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <configuration>
                    <!--generatorConfig.xml檔案位置-->
                    <configurationFile>src/main/resources/mybatis-generator/generatorConfig.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <executions>
                    <execution>
                        <id>Generate MyBatis Artifacts</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <phase>generate-sources</phase>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.44</version>
                        <scope>runtime</scope>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>
           

有些依賴是後面添加進去的,例如druid相關包。

3.Mybatis自動生成代碼

Mybatis是需要通過xml和Mapper映射來完成正常使用,以前使用hibernate的時候,可以通過eclipse自動逆向工程生成各個表的hbm檔案,Mybatis也提供的有類似的功能,通過maven插件來自動生成每一個表的實體類、對應的Mapper以及xml檔案。

關于Mybatis Generator配置檔案的詳細介紹,參考這裡,部落客寫的非常詳細,下面根據需要我在項目中的使用如下。

3.1 建立generatorCOnfig檔案

在resources目錄下建立mybatis-generator目錄,然後建立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>
    <!--執行generator插件生成檔案的指令: call mvn mybatis-generator:generate -e -->
    <!--classPathEntry:資料庫的JDBC驅動,換成你自己的驅動位置 可選 -->
    <!--<classPathEntry location="E:\mybatis\mysql-connector-java-5.1.24-bin.jar" /> -->

    <!-- 一個資料庫一個context -->
    <!--defaultModelType="flat" 大資料字段,不分表 -->
    <context id="MysqlTables" targetRuntime="MyBatis3Simple" defaultModelType="flat">
        <!-- 自動識别資料庫關鍵字,預設false,如果設定為true,根據SqlReservedWords中定義的關鍵字清單;
        一般保留預設值,遇到資料庫關鍵字(Java關鍵字),使用columnOverride覆寫 -->
        <property name="autoDelimitKeywords" value="true"/>
        <!-- 生成的Java檔案的編碼 -->
        <property name="javaFileEncoding" value="utf-8"/>
        <!-- beginningDelimiter和endingDelimiter:指明資料庫的用于标記資料庫對象名的符号,比如ORACLE就是雙引号,MYSQL預設是`反引号; -->
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>

        <!-- 格式化java代碼 -->
        <property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
        <!-- 格式化XML代碼 -->
        <property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>

        <!-- 注釋 -->
        <commentGenerator>
            <property name="suppressAllComments" value="false"/><!-- 是否取消注釋 -->
            <property name="suppressDate" value="true"/> <!-- 是否生成注釋代時間戳-->
        </commentGenerator>

        <!-- jdbc連接配接 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://130.76.53.161:3306/customer"
                        userId="root"
                        password="root"/>
        <!-- 類型轉換 -->
        <javaTypeResolver>
            <!-- 是否使用bigDecimal, false可自動轉化以下類型(Long, Integer, Short, etc.) -->
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!-- 生成實體類位址 -->
        <javaModelGenerator targetPackage="com.tydic.cloud_wind.models" targetProject="src/main/java">
            <property name="enableSubPackages" value="false"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- 生成mapxml檔案 -->
        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>
        <!-- 生成mapxml對應client,也就是接口dao -->
        <javaClientGenerator targetPackage="com.tydic.cloud_wind.dao" targetProject="src/main/java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <!-- table可以有多個,每個資料庫中的表都可以寫一個table,tableName表示要比對的資料庫表,也可以在tableName屬性中通過使用%通配符來比對所有資料庫表,隻有比對的表才會自動生成檔案 -->
        <!--如果想生成一個表則tableName="table_name"-->
        <table tableName="%"
               enableCountByExample="true"
               enableUpdateByExample="true"
               enableDeleteByExample="true"
               enableSelectByExample="true"
               selectByExampleQueryId="true">
            <property name="useActualColumnNames" value="false"/>
        </table>
    </context>
</generatorConfiguration>
           

我在用的時候有幾個疑惑點:

(1)context的targeRuntime是什麼,一定要用MyBatis3Simple嗎?

不單單有MyBatis3SImple,還有MyBatis3,差別在于:

targetRuntime:
        1,MyBatis3:預設的值,生成基于MyBatis3.x以上版本的内容,包括XXXBySample;
        2,MyBatis3Simple:類似MyBatis3,隻是不生成XXXBySample;
           

(2)如果後面有表結構新增了,我能不能一個表一個表的添加?

可以,在配置檔案中,關于table的配置,可以單獨配置獨立的表,如下所示:

<table tableName="user_info" domainObjectName="UserInfo"></table>
           

注意修改targetProject中對應的目錄,以及對應的檔案夾需要提前建立好,例如:mapper/models/dao在相關包或者目錄下的檔案夾。

(3)假設資料庫表設計的不規範,有些字段名稱和java的關鍵字一樣,例如:有字段名為public,此時生成的實體類就會有錯,怎麼解決?

可以通過再table中配置columnOverride來解決這個問題

<table tableName="%"
               enableCountByExample="true"
               enableUpdateByExample="true"
               enableDeleteByExample="true"
               enableSelectByExample="true"
               selectByExampleQueryId="true">
        <columnOverride column="public" property="isPublic" />
 </table>
           

3.2 pom.xml檔案中添加插件

添加在build下的plugins下,内容如下

<!--添加mybatis generator maven插件,自動生産mybatis對應的實體類-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <configuration>
                    <!--generatorConfig.xml檔案位置-->
                    <configurationFile>src/main/resources/mybatis-generator/generatorConfig.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <executions>
                    <execution>
                        <id>Generate MyBatis Artifacts</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <phase>generate-sources</phase>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.44</version>
                        <scope>runtime</scope>
                    </dependency>
                </dependencies>
            </plugin>
           

主要是指定generatorConfig配置檔案的位置。

3.3 開始生成

有兩種操作方式,第一種是在idea中通過界面化操作來完成,如下:

SpringBoot開發記錄1.快速初始化2.pom.xml檔案3.Mybatis自動生成代碼4.查詢樣例5.關于事物6.druid監控7.Controller注:8.Swagger9.關于restful10.關于全局異常捕獲參考文獻

點選按鈕之後,就能看到每個目錄下的已經生成的檔案的,一個表對應一個dao、Mapper、xml檔案。

也可以直接使用maven指令,指令如下

mvn mybatis-generator:generate
           

3.4 使用

相關代碼生成之後,其實還沒有和SpringBoot建立聯系,因為其實相關實體類及映射檔案的生成,其實和Springboot架構自身并沒有關系,單獨使用maven也可以生成,是以,在生成相關檔案之後,我們還需要有一些其他配置。

(1)在啟動類添加注解

/**
 * 配置MapperScan就不用再每一個Mapper檔案上添加@Mapper的注解了
 */
@SpringBootApplication
@MapperScan("com.tydic.cloud_wind.dao")
public class CloudWindApplication {
	public static void main(String[] args) {
		SpringApplication.run(CloudWindApplication.class, args);
	}

}
           

(2)指定mapper的映射檔案

在application.properties中添加如下一行設定

#指定mapper映射檔案位置
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
#指定models的位置,如果在mapper.xml檔案中的resultType指定了具體的包路徑,這裡就可以不用設定
#mybatis.type-aliases-package=com.tydic.cloud_wind.models
           

關于mybatis.type-aliases-package的配置,如果是通過自動生成的代碼,是不需要配置的,因為我們随意打開一個xml檔案,可以看到其namespace指定的都是具體的包路徑下的Mapper檔案,但是有時候,我們可以還會自定義一些Mapper檔案和xml檔案,此時如果namespace僅僅指定類名,而不指定具體的包路徑,就需要在application.properties中配置mybatis.type-aliases-package,這樣xml檔案就能和Mapper檔案自動建立映射。

4.查詢樣例

編寫查詢相關代碼之前,我們還需要先設定mybatis的資料源相關資訊,編輯application.properties,如下:

#資料庫配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://192.168.1.120:3306/customer?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
#spring.datasource.filters=stat,wall,log4j

#mybatis配置
#開啟二級緩存 一級緩存是mapper級别,二級緩存是namespaces級别的,所謂緩存就是相同查詢不走資料庫,除非觸發了清楚緩存動作才會重新查詢
mybatis.configuration.cache-enabled=true
mybatis.configuration.default-statement-timeout=3000
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true
#指定mapper映射檔案位置
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
#指定models的位置,如果在mapper.xml檔案中的resultType指定了具體的包路徑,這裡就可以不用設定
#mybatis.type-aliases-package=com.tydic.cloud_wind.models
#開啟列印sql查詢日志的功能
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
           

4.1 普通單表操作

在dao下對應每一個表生成的Mapper檔案,都有如5個方法:

public interface DdMsgPushMapper {
    int deleteByPrimaryKey(Integer id);
    int insert(DdMsgPush record);
    DdMsgPush selectByPrimaryKey(Integer id);
    List<DdMsgPush> selectAll();
    int updateByPrimaryKey(DdMsgPush record);
}
           

我們在使用的時候,可以非常友善的通過需要構造對應的實體類,然後完成對單表的增删改查,下面看幾個簡單的例子,在service包下新增一個MybatisService的類,部分樣例查詢代入如下:

@Service
@Transactional
public class MybatisService
{
    @Autowired
    private TmpDispUserMapper   tMapper;

    //簡單查詢-直接查詢全部
    public void simpleSelect()
    {
        List<TmpDispUser> rList = tMapper.selectAll();
        System.out.println(rList.size());
        for (TmpDispUser tU : rList)
        {
            System.out.println(tU.toString());
        }
    }

    //簡單查詢-根據id查詢
    public void findUserById(Integer id)
    {
        TmpDispUser tUser = tMapper.selectByPrimaryKey(id);
        System.out.println(tUser.toString());
    }

    //正常新增
    public void addUser()
    {
        TmpDispUser tUser = new TmpDispUser();
        tUser.setName("劉ddd劉洋洋");
        tUser.setTel("18654745d21");
        Integer rId = tMapper.insert(tUser);
        //給Mapper.xml中的insert添加useGeneratedKeys="true" keyProperty="id"這倆屬性,tUser.getId()就能擷取插入後的自增id
        System.out.println(tUser.toString());
        System.out.println(rId);
    } 
}
           

可以看到,相關操作還是非常的簡單和便捷。但實際的開發中,往往聯合查詢多表操作的應用場景更多。

4.2 自增主鍵表的操作

如果資料庫中相關表的主鍵是自增的,如何操作?看上面的代碼addUser方法中,就是操作自增主鍵表的樣例,通過構造一個實體類,然後調用insert方法,插入。有以下幾點注意事項:

(1)實體類對應自增主鍵的成員不用調set方法,空的就行

(2)對應的xml檔案要修改insert語句,添加userGeneratedKeys="true" keyProperty="id",keyProperty指定的是對應表的主鍵名稱。如下:

<insert id="insert" parameterType="com.tydic.cloud_wind.models.TmpDispUser" useGeneratedKeys="true" keyProperty="id">
    insert into tmp_disp_user (id, `name`, tel
      )
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{tel,jdbcType=VARCHAR}
      )
  </insert>
           

在完成insert操作之後,入參的實體對象,通過調用get方法就可以直接擷取插入進去記錄的主鍵id。

4.3 聯合查詢

前面提到,在實際的開發中,多表的聯合查詢以及不規則查往往比單表操作要多的多,對于我而言,我更喜歡使用Map作為查詢的結果,下面記錄下Mybatis的自定義查詢開發。網上有有一些方案是通過對實體類的構造來完成的,例如關聯查詢的時候,會用某個實體類所有最大類,然後進行傳回,這種有利有弊,好處在于可直接操作對象,不友善之處在于代碼量大,而且總要去做類的開發,參考這裡。

聯合查詢或者說自定義查詢,有兩種方式,一種是通過xml檔案配置相關的查詢sql,一種是使用注解方式。

(1) 自定義xml

即:我們可以自己編寫相關xml檔案,然後指定對應的Mapper接口檔案。先開看一個例子:

我在dao包下新增一個CommDaoMapper接口,定義如下幾個方法:

/**
 * 公用三個查詢方法
 * 1.結果傳回是Map<String,Object>
 * 2.結果傳回是List<Map<String,Object>>
 * 3.分頁查詢
 */
public interface CommDaoMapper
{
    //單獨參數查詢,出參是Map
    Map<String,Object> selectByParams(String p1);
    //單獨參數查詢,出單是實體類
    TmpServerUser selectByParamsWithBean(@Param("pid") String pid);
    //Map查詢參數,出參是List
    List<Map<String,Object>> selectWithList(Map<String,Object> p1);
    //獨立入參,表名是動态的,出參是List
    List<Map<String,Object>> selectWithListByDt(Map<String,Object> p1);
}
           

然後在resources/mapper目錄下新增一個CommDaoMapper.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.tydic.cloud_wind.dao.CommDaoMapper" >

    <!--多個獨立參數查詢方式-->
    <!--一個Map查詢方式-->
    <!--動态指定表明查詢方式-->
    <!--多表聯合查詢-->
    <!--分頁查詢-->
    <select id="selectByParams"
            resultType="java.util.Map"
            parameterType="java.lang.String" >
        select id,s_id,name,tel
        from tmp_server_user
        where id = #{0}
    </select>
    <select id="selectByParamsWithBean"
            resultType="com.tydic.cloud_wind.models.TmpServerUser"
            parameterType="java.lang.String" >
        select *
        from tmp_server_user
        where id = #{pid}
    </select>
    <select id="selectWithList"
            resultType="java.util.Map"
            parameterType="java.util.Map" >
        select *
        from tmp_disp_user a,tmp_server_user b
        where a.id=b.id and a.name like '%' || #{u_name} || '%'
    </select>
    <select id="selectWithListByDt"
            resultType="java.util.Map"
            parameterType="java.util.Map" >
        select *
        from ${t_name} a
        where a.name like '%' || #{u_name} || '%'
    </select>
</mapper>
           

然後在MybatisService中寫一個測試方法如下:

@Autowired
    private CommDaoMapper       cMapper;
    //自定義的查詢
    public void selectWithDsign()
    {
        //selectByParams
        Map<String,Object> rMap = cMapper.selectByParams("2");
        System.out.println(rMap.toString());
        //selectByParamsWithBean
        TmpServerUser tUser = cMapper.selectByParamsWithBean("2");
        System.out.println(tUser.toString());
        //selectWithList
        Map<String,Object> p1 = new HashMap<>();
        p1.put("u_name","洋");
        List<Map<String,Object>> rList = cMapper.selectWithList(p1);
        System.out.println(rList.toString());
        //selectWithListByDt
        Map<String,Object> p2 = new HashMap<>();
        p2.put("t_name","tmp_disp_user");
        p2.put("u_name","洋");
        List<Map<String,Object>> rL2 = cMapper.selectWithListByDt(p2);
        System.out.println(rL2);
    }
           

這裡有一個注意點,那就是在使用Map傳參的時候,可以直接使用#{參數名}的方式使用參數,在直接獨立傳參的時候可以使用#{參數順序的數字}方式傳參。

而參數的傳遞,可以使用${}也可以使用#{},他們的差別在于:

#是将闖入的值當做字元串形式,很大程度上可以防止sql注入,

$是将傳入的資料直接顯示成sql語句,即:sql是需要動态的,參數是表名、關鍵字、或者order by後要使用$,(預編譯前)

  #{}: 解析為一個 JDBC 預編譯語句(prepared statement)的參數标記符,一個 #{ } 被解析為一個參數占位符 。

   ${}: 僅僅為一個純碎的 string 替換,在動态 SQL 解析階段将會進行變量替換。

(2)注解方式

個人感覺注解方式更簡單一些,不用配置xml檔案,當然,不配置xml檔案有些mybatis的進階特性可能也沒法使用,各有利弊吧,樣例如下:

public interface CommDaoMapper
{
    //--通過注解方式進行資料庫的操作,不用對應的xml檔案
    @Select("select * from tmp_disp_user")
    List<TmpDispUser> findAllTmpUser();

    @Select("select * from tmp_disp_user where id= #{id}")
    TmpDispUser findTmpUserById(@Param("id") int id);

    @Update("update tmp_disp_user set name=#{name} where id=#{id}")
    int updateTmpUser(TmpDispUser tUser);

    @Select("select * from tmp_disp_user a,tmp_server_user b where a.id=b.id and a.name like '%' || #{u_name} || '%'")
    List<Map<String,Object>> findWithList(Map<String,Object> p1);

    @Select("select * from ${t_name} a where a.name like '%' || #{u_name} || '%'")
    List<Map<String,Object>> findWithListByDt(Map<String,Object> p1);
}
           

這樣就行了,很簡單。

4.4 分頁查詢

MyBatis的分頁查詢推薦使用pagehelper插件來完成,這個插件非常的好用,可以無侵入的完成分頁,不用修改原來全量查詢的sql語句和xml檔案。隻需要在使用的時候設定下分頁相關資訊即可。具體參考這裡。

(1)pom.xml中增加依賴

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.2.7</version>
</dependency>
           

(2)編寫自動配置類

@Configuration//将該類加到spring容器裡
public class PageHelperConfig {
    @Bean//加上該注解spring容器自動配置
    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("offsetAsPageNum", "true");
        properties.setProperty("rowBoundsWithCount", "true");
        properties.setProperty("reasonable", "true");
        properties.setProperty("dialect","mysql");
        pageHelper.setProperties(properties);
        return pageHelper;
    }
}
           

(3)使用樣例

使用的時候非常的友善,隻需要在調用原來正常查詢之前添加startPage方法即可,如下所示:

public void testFY1()
{
     //xml單表分頁查詢
        PageHelper.startPage(1,5);
        List<QeUserAsk> rList = qAsk.selectAll();
        PageInfo<SysUserInfo> page = new PageInfo<>(rList);
        System.out.println("總數量:" + page.getTotal());
        System.out.println("目前頁查詢記錄:" + page.getList().size());
        System.out.println("目前頁碼:" + page.getPageNum());
        System.out.println("每頁顯示數量:" + page.getPageSize());
        System.out.println("總頁:" + page.getPages());
        System.out.println(rList);
}
           

如上就是查詢第一頁,每頁5條資料。而selectAll是查詢全量資料的sql。是以可以說是無侵入的分頁插件,很友善。

注意:一個方法中隻能使用一次startPage和對應的一次查詢,不能查詢多次。

5.關于事物

SpringBoot的事物管理以及傳播網上有很多非常優秀的博文,這裡貼幾個比較好的。我主要記錄下我在學習過程中對遇到的一些點和認識。

【事務相關】SpringBoot之資料庫(四)——事務處理:隔離級别與傳播行為

【事務相關】原理剖析

【事務相關】資料庫-事務和鎖

【事務相關】SpringBoot之事務處理機制

【事務相關】SpringBoot 資料庫事務7種傳播行為

【事務相關】spring事務@Transactional在同一個類中的方法調用不生效

5.1 關于@EnableTransactionManagement注解

網上不少博文都提到,要使用事物,需要先通過@EnableTransactionManagement注解開啟事物,然後再對應的類上使用@Transactional注解。其實是@EnableTransactionManagement并不會必須的。SpringBoot會開啟自動配置的事物管理器,如果你使用JDBC或者JPA作為資料庫通路技術,就會自動開啟@EnableTransactionManagement這個注解,是以不用再顯示的調用了。隻需要在進行事物管理的類上添加@Transactional注解即可。

5.2 關于@Transactional注解的位置

注解最好是放置在類上,而不是方法上,如果放到方法上,有可能會有一些bug的存在,例如:

A類中方法1和方法2,給方法1添加事物注解,方法2不添加,但是方法2中會調用方法1,這之後我們測試,如果出問題了,

事物是不會復原的。因為我們在調用方法2的時候,方法2并沒有被攔截,而方法2中調用方法1屬于類内部調用,也不會觸動方法攔截,是以事物失效。

5.3 關于try/catch

不要在service中想進行事物控制的方法中進行try/catch,因為如果你自己攔截了異常,事物攔截器就無法正常工作了,進而事務也就失效了,最好的辦法是把異常抛出去,在控制層處理,或者通過切面全局處理。

5.4 關于事物傳播

SpringBoot有7中傳播行為,預設是REQUIRED(0),即:如果目前存在事物,就沿用目前事物,否則建立一個事物運作子方法,在方法嵌套執行過程中,隻要有一個出錯,全部都會回退。

常用的還有REQUIRES_NEW(3),無論目前事務是否存在,都會建立新事務運作方法,新事務可以擁有新的鎖和隔離級别等特性,與目前事務互相獨立。誰出錯了,誰回退。

還有NESTED(6),在目前方法調用子方法時,如果子方法發生異常,隻復原子方法執行過的SQL,而不復原目前方法的事務。

6.druid監控

寫一個自動配置類即可,代碼如下:

package com.tydic.cloud_wind.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * @Author: Zhouc52
 * @Description:
 * @Date: Create in 10:40 2019/4/19
 * @Modified by:
 * 項目正常啟動後,浏覽器輸入:
 * http://127.0.0.1:8080/druid  輸入使用者名密碼即可通路
 */
@Configuration
public class DruidConfig
{
    @Bean public ServletRegistrationBean druidServlet()
    {
        // 主要實作WEB監控的配置處理
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); // 現在要進行druid監控的配置處理操作
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1,192.168.1.116"); // 白名單
        servletRegistrationBean.addInitParameter("deny", "192.168.1.200"); // 黑名單
        servletRegistrationBean.addInitParameter("loginUsername", "admin"); // 使用者名
        servletRegistrationBean.addInitParameter("loginPassword", "admin"); // 密碼
        servletRegistrationBean.addInitParameter("resetEnable", "false"); // 是否可以重置資料源
        return servletRegistrationBean;
    }

    @Bean public FilterRegistrationBean filterRegistrationBean()
    {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*"); // 所有請求進行監控處理
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*");
        return filterRegistrationBean;
    }

    /**
        這裡@Bean如果不帶參數,在進入druid的監控頁的時候,會看到資料源未設定的錯誤提示,
        但是前台操作請求過一次資料庫,這個錯誤就消失了,存在這樣的問題在于mybatis可以獨立SpringBoot
        使用,是需要指定資料源關閉方法以及初始化方法的,否則druid首次會擷取資料源失敗,待Spring有請求
        之後,druid從SpringBoot擷取到SpringBoot接管的資料源,是以就顯示正常了
     */

    @Bean(destroyMethod = "close",initMethod = "init")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource()
    {
        return new DruidDataSource();
    }
}
           

項目啟動之後,直接在浏覽器中輸入:

http://127.0.0.1:8080/druid
           

輸入使用者名密碼,即可通路,這個監控非常強大,截圖如下:

SpringBoot開發記錄1.快速初始化2.pom.xml檔案3.Mybatis自動生成代碼4.查詢樣例5.關于事物6.druid監控7.Controller注:8.Swagger9.關于restful10.關于全局異常捕獲參考文獻

其中druidDataSource設定@Bean注解的時候,要添加參數,否則你會發現首次進入點選資料源,會有以下錯誤:

SpringBoot開發記錄1.快速初始化2.pom.xml檔案3.Mybatis自動生成代碼4.查詢樣例5.關于事物6.druid監控7.Controller注:8.Swagger9.關于restful10.關于全局異常捕獲參考文獻

注:

有些時候可能會出現SQL監控中無資料,看不到sql查詢的監控的情況。主要問題在于,首先要在application.properties中添加:

spring.datasource.filters=stat,wall,log4j
           

然後如果沒有引入log4j的依賴,要在pom.xml中引入log4j的依賴:

<dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-log4j12</artifactId>
</dependency>
           

然後再檢視,可以正常監控sql了。

7.Controller

控制器通過@RestController注解來聲明,這裡我主要寫了幾個樣例,因為控制器中主要涉及的就是接口的入參和出參。其中出參的話SpringBoot會自動幫我們轉換成JSON對象。入參相關樣例如下:

package com.tydic.cloud_wind.controller;

import com.tydic.cloud_wind.models.TestLable;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 入參的幾種情況
 * 1.使用HttpServletRequest req擷取參數
 * 2.使用@RequestParam注解擷取參數
 * 3.使用@RequestBody擷取參數
 */

@RestController
@RequestMapping("/index")
public class IndexController
{
    /**
     * 前端請求時候Content-type=application/x-www-form-urlencoded;charset=UTF-8
     * 可以使用post或者get,入參要有參數名p1,p2
     * 可以是送出表單、也可以是普通的ajax送出
     * 如果使用axios,需要使用QS.stringify(params)規格化
     * params:
     * {
     *     "p1":"1",
     *     "p2":"2"
     * }
     *
     */
    @RequestMapping(value = "/t1",method = {RequestMethod.GET,RequestMethod.POST})
    public Object test1(HttpServletRequest req)
    {
        String p1 = req.getParameter("p1");
        String p2 = req.getParameter("p2");
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result","參數1="+p1+",參數2="+p2);
        return rMap;
    }

    /**
     * 同t1規則一樣,需要說明的是,看到有些文章說@RequestParam無法接收到POST請求的參數,其實并不是
     * 隻要Content-type=application/x-www-form-urlencoded;charset=UTF-8,都是可以的.預設參數的required都是必填,可以設定為false,調整為非必填。如果入參的參數名和這裡參數名一緻,也可以省略掉@RequestParam的注解。
     */
    @RequestMapping(value = "/t2",method = {RequestMethod.GET,RequestMethod.POST})
    public Object test2(@RequestParam(defaultValue = "") String p1,@RequestParam(required = false) String p2)
    {
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result","參數1="+p1+",參數2="+p2);
        return rMap;
    }

    /**
     * 前端請求的時候設定Content-type=application/json
     * 隻支援post請求
     * 如果使用axios送出,則直接送出即可,一定不能再使用QS.stringify(params)規格化,
     * 不然後端收不到參數,這種方式,SpringBoot會自動把入參轉換成對象
     */
    @PostMapping(value = "/t3")
    public Object test3(@RequestBody Map<String,Object> p1)
    {
        System.out.println(p1.toString());
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result",p1);
        return rMap;
    }

    /**
     * 同test3方法,不同之處在于,也可以指定Content-type=text/plain或者其他格式
     * 收到的入參String是什麼就是什麼,不會再做特殊處理。
     */
    @PostMapping(value="/t4")
    public Object test4(@RequestBody String p1)
    {
        System.out.println(p1);
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result",p1);
        return rMap;
    }

    /**
     * 同test3,不同之處在于,要求入參的json格式必須和實體類對的上,否則擷取不到入參參數
     * 關于這種,假設前端入參的參數名和後端對象的成員名稱一緻,這裡可以省略掉@RequestBody,同時前端請求的時候,也可以test1那樣的請求,接口這邊Spring會自動處理。
     */
    @PostMapping(value="/t5")
    public Object test5(@RequestBody TestLable p1)
    {
        System.out.println(p1.toString());
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("result",p1);
        return rMap;
    }
}
           

注:

8.Swagger

Swagger絕對是神器,在使用Swagger之前,接口的描述都需要通過word文檔進行定義和描述,要寫清楚出參和入參,對于接口對接方,還需要使用各種工具進行接口的測試,

但是項目內建Swagger之後,這一切的一切都可以省掉了,Swagger是一個可視化的接口文檔描述插件,隻需要接口開發人員在接口代碼上添加相關的注解說明,接口調用方

就可以通過界面化的平台看到所有的接口說明,并且平台還提供的線上測試功能,簡直爽的無以複加。下面記錄下項目中如何引入Swagger以及如何使用。

8.1 添加maven依賴

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

8.2 添加自動配置類

@Configuration
@EnableSwagger2
public class Swagger2
{
    @Bean
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.tydic.cloud_wind"))
                .paths(PathSelectors.any())
                .build();
    }
    //通路url的端口号和服務的端口号一樣,隻不過後面多一個/swagger-ui.html
    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("Spring Boot基礎架構內建Swagger2")
                .description("使用Swagger2測試接口及接口說明檢視")
                .termsOfServiceUrl("http://localhost:8080/swagger-ui.html")
                .contact(new Contact("zhouc52","http://sina.com","[email protected]"))
                .version("1.0")
                .build();
    }
}
           

注意basePackage的設定,一定要設定正确,不然看不到相關接口說明。

8.3 給接口配置說明

@RestController
@RequestMapping("/index")
@Api(value = "indexController控制器相關api" , description = "這裡是描述資訊,可以自己填寫")
public class IndexController
{
    /**
     * 前端請求時候Content-type=application/x-www-form-urlencoded;charset=UTF-8
     * 可以使用post或者get,入參要有參數名p1,p2
     * 可以是送出表單、也可以是普通的ajax送出
     * 如果使用axios,需要使用QS.stringify(params)規格化
     * params:
     * {
     *     "p1":"1",
     *     "p2":"2"
     * }
     *
     */
    @ApiOperation(value="測試接口1",notes = "測試接口1,入參通過HttpServletRequest req方式擷取")
    @ApiImplicitParams({
            @ApiImplicitParam(name="p1",value = "參數1",dataType = "String",paramType = "query",required = true),
            @ApiImplicitParam(name="p2",value = "參數2",dataType = "String",paramType = "query")
    })
    @RequestMapping(value = "/t1",method = {RequestMethod.GET,RequestMethod.POST})
    public Object test1(HttpServletRequest req)
    {
        String p1 = req.getParameter("p1");
        String p2 = req.getParameter("p2");
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result","參數1="+p1+",參數2="+p2);
        return rMap;
    }

    /**
     * 同t1規則一樣,需要說明的是,看到有些文章說@RequestParam無法接收到POST請求的參數,其實并不是
     * 隻要Content-type=application/x-www-form-urlencoded;charset=UTF-8,都是可以的
     */
    @ApiOperation(value="測試接口2",notes = "測試接口2,入參通過@RequestParam方式擷取")
    @ApiImplicitParams({
            @ApiImplicitParam(name="p1",value = "參數1",dataType = "String",paramType = "query",required = true),
            @ApiImplicitParam(name="p2",value = "參數2",dataType = "String",paramType = "query")
    })
    @RequestMapping(value = "/t2",method = {RequestMethod.GET,RequestMethod.POST})
    public Object test2(@RequestParam String p1,@RequestParam String p2)
    {
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result","參數1="+p1+",參數2="+p2);
        return rMap;
    }

    /**
     * 前端請求的時候設定Content-type=application/json
     * 隻支援post請求
     * 如果使用axios送出,則直接送出即可,一定不能再使用QS.stringify(params)規格化,
     * 不然後端收不到參數,這種方式,SpringBoot會自動把入參轉換成對象
     */
    @ApiOperation(value="測試接口3",notes = "測試接口3,入參通過@RequestBody擷取對象")
    @ApiImplicitParam(name = "p1",value = "查詢的Map對象")
    @PostMapping(value = "/t3")
    public Object test3(@RequestBody Map<String,Object> p1)
    {
        System.out.println(p1.toString());
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result",p1);
        return rMap;
    }

    /**
     * 同test3方法,不同之處在于,也可以指定Content-type=text/plain或者其他格式
     * 收到的入參String是什麼就是什麼,不會再做特殊處理。
     */
    @PostMapping(value="/t4")
    public Object test4(@RequestBody String p1)
    {
        System.out.println(p1);
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("des","成功!");
        rMap.put("result",p1);
        return rMap;
    }

    /**
     * 同test3,不同之處在于,要求入參的json格式必須和實體類對的上,否則擷取不到入參參數
     *
     */
    @ApiOperation(value="測試接口5",notes = "測試接口5,入參是實體類通過@RequestBody擷取對象")
    @ApiImplicitParam(name = "p1",value = "實體類",dataType = "TestLable")
    @PostMapping(value="/t5")
    public Object test5(@RequestBody TestLable p1)
    {
        System.out.println(p1.toString());
        Map<String,Object> rMap=new HashMap<>();
        rMap.put("code","0");
        rMap.put("result",p1);
        return rMap;
    }
}
           

8.4 界面化接口說明文檔

在浏覽器中輸入:http://localhost:8080/swagger-ui.html

SpringBoot開發記錄1.快速初始化2.pom.xml檔案3.Mybatis自動生成代碼4.查詢樣例5.關于事物6.druid監控7.Controller注:8.Swagger9.關于restful10.關于全局異常捕獲參考文獻

9.關于restful

Restful api是一種規範,不是什麼新技術。我對restful的簡單了解就是通過url可以直接看出請求是什麼資源,簡而言之就是以前強調動作,現在強調資源,

然後通過使用http的不同請求辨別不同動作,例如:get是查詢,post是送出修改、delete是删除、put是修改等等。然後url的末尾是資源辨別。

但現實中,這種方式并不是很理想,例如:我想批量删除怎麼辦?我的一個動作要更新多個訂單的狀态,此時怎麼設計?等等。

其實,我們平常用的http post請求也是restful api,隻不過不規範而已,但是,滿足需求,并能快速解決問題就是好的方案。

這篇博文說的也很好,可以參考下。

10.關于全局異常捕獲

為了避免服務直接報異常,對服務調用方産生不好的體驗,可以添加一個全局異常捕獲類,對于運作過程中由控制器抛出的異常,我們都可以攔截到,并給出統一的提示,這樣在一定程度上增加友好度。當然,最好還是背景開發人員在寫代碼的時候能自己對自己的代碼負責,處理好相關異常邏輯。

增加全局異常攔截的非常簡單,寫一個配置類即可,如下所示:

@ControllerAdvice
public class GlobalExceptionHandler
{
    @ExceptionHandler({RuntimeException.class})
    @ResponseBody
    public ResultJson exceptionHandler(RuntimeException e,HttpServletResponse resp)
    {
        ResultJson rJson = new ResultJson();
        rJson.setCode(CONST.FAILE);
        rJson.setDescription("很遺憾,伺服器開小差了,請稍後再試!");
        rJson.setResult(e.getMessage());
        return rJson;
    }

    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    @ResponseBody
    public ResultJson supportedException(HttpRequestMethodNotSupportedException e,HttpServletResponse resp)
    {
        ResultJson rJson = new ResultJson();
        rJson.setCode(CONST.FAILE);
        rJson.setDescription("很遺憾,服務不支援GET請求!");
        rJson.setResult(e.getMessage());
        return rJson;
    }
}
           

參考文獻

【1】Mybatis Generator最完整配置詳解

【2】SpringBoot整合MyBatis

【3】Springboot mybatis generate 自動生成實體類和Mapper

【4】SpringBoot----用MyBatis注解實作資料的增删查改

【5】Mybatis 中$與#的差別

【6】springboot整合mybatis進行分頁查詢

【7】SpringBoot整合Swagger2