目錄
5.1 關於@EnableTransactionManagement註解
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之數據庫(四)——事務處理:隔離級別與傳播行爲
【事務相關】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,只不過不規範而已,但是,滿足需求,並能快速解決問題就是好的方案。
這篇博文說的也很好,可以參考下。
參考文獻
【3】Springboot mybatis generate 自動生成實體類和Mapper