SpringBoot開發記錄

目錄

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

參考文獻


1.快速初始化

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

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

選完之後,點擊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.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中通過界面化操作來完成,如下:

點擊按鈕之後,就能看到每個目錄下的已經生成的文件的,一個表對應一個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://130.76.53.161: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(String p1);
    //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 = #{0}
    </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();
     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

輸入用戶名密碼,即可訪問,這個監控非常強大,截圖如下:

其中druidDataSource設置@Bean註解的時候,要添加參數,否則你會發現首次進入點擊數據源,會有以下錯誤:

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,都是可以的
     */
    @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會自動把入參轉換成對象
     */
    @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格式必須和實體類對的上,否則獲取不到入參參數
     *
     */
    @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

9.關於restful

Restful api是一種規範,不是什麼新技術。我對restful的簡單理解就是通過url可以直接看出請求是什麼資源,簡而言之就是以前強調動作,現在強調資源,
然後通過使用http的不同請求標識不同動作,例如:get是查詢,post是提交修改、delete是刪除、put是修改等等。然後url的末尾是資源標識。
但現實中,這種方式並不是很理想,例如:我想批量刪除怎麼辦?我的一個動作要更新多個訂單的狀態,此時怎麼設計?等等。
其實,我們平常用的http post請求也是restful api,只不過不規範而已,但是,滿足需求,並能快速解決問題就是好的方案。
這篇博文說的也很好,可以參考下。

參考文獻

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

【2】SpringBoot整合MyBatis

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

【4】SpringBoot----用MyBatis註解實現數據的增刪查改

【5】Mybatis 中$與#的區別

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

【7】SpringBoot整合Swagger2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章