MyBatis-Plus的一些高級乾貨
MyBatis Plus(簡稱 MP)是一個 MyBatis 的增強版,在 MyBatis 的基礎上只做增強不做改變,爲簡化開發、提高效率而生。
記住:
mp官方文檔:https://mp.baomidou.com/guide/
1.MyBatis-Plus與MyBatis的區別
- 對於實體類而言,需要加一定的註解。
- MyBatis-Plus通過讓實體類名與表名關聯,與表字段關聯。
- 通用性不同,MyBatis是外國人開發的,國內外皆通用,而MyBatis-Plus是由國內人士自發組織的開源,目前主要通用於國內;
- ,
注意:mybatis-plus會自動維護mybatis以及mybatis-spring的依賴,所以不需要引入後兩者,避免發生版本衝突. 只需把mybatis的依賴換成mybatis-plus的依賴。
2.全局唯一id的生成
Mybatis plus 在調用插入方法時,會自動生成id,是一個全局唯一id,一起插入到數據表中。
mp默認新增的時候,產生的是全局唯一id,採用雪花算法生成,自3.3.0開始,默認使用雪花算法+UUID。(Twitter的snowflake算法又名雪花算法,生成long類型的唯一數字)
如圖:
而這個唯一id的生成,常常用來做我們的數據庫字段的主鍵~!
3.主鍵生成策略:
在日常開發中,對於一些數據庫中的主鍵,我們一般會採用 等方式,
具體對比:分佈式系統唯一ID生成方案。
mp自3.3.0開始,默認使用雪花算法+UUID(不含中劃線)。
主鍵生成策略必須使用INPUT。
設置生成的主鍵是自增樣式:
(在mp生成id時,根據上一個id的數值,而自增)
實現過程:
3.1實體類上添加註解:
@TableId(type = IdType.INPUT)
private Long id;
如圖:
3.2數據庫字段設置爲自增
結果:
Mp中其他的id屬性的解釋:
一旦手動輸入id之後,就需要自己配置id生成方法了!不然就可以用這些註解來生成id自動填入數據庫。
4.自動填充:
開發中,一般數據庫總有一些字段我們想要的是。比如創建時間,修改時間等字段!這些個操作一般都是自動化完成的,企業級都不希望手動更新。
(在阿里巴巴開發手冊中就有寫:所有的數據庫表gmt_create,gmt_modified 字段幾乎都要有,而自動化填入,便於追蹤)
實現方式
4.1 方式一:數據庫級別 (工作中不建議)
- 比如在表中新增字段create_time, update_time。只需要設置默認的填充方式即可。
結果:
4.2 方法二:代碼級別 (推薦)
-
刪除數據庫的該字段的默認值,更新操作等設置(上一種方式我設置了,我恢復一下而已)。
(恢復如圖下這樣)
-
添加註解
實體類上加個註解,一個是新增,一個是更新。// 設置填充字段的填充類型 @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime;
-
編寫處理器來處理這個註解,自定義實現類來實現implements MetaObjectHandler()
配置類源碼如下:
@Slf4j @Component // 一定不要忘記把處理器加入到IOC容器中。 public class MyMetaObjectHandler implements MetaObjectHandler { // 插入時候的填充策略 @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill ...."); this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); //版本 3.3.0(推薦使用) } // 更新時間的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill ...."); this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推薦使用) } }
程序會通過反射去讀取實體類中對應的註解,填入內容。
注意事項:
• 字段必須聲明TableField註解,屬性fill選擇對應策略,該聲明告知MybatisPlus需要預留注入SQL字段。
• 填充處理器MyMetaObjectHandler在SpringBoot中需要聲明@Component註解或@Bean注入。
填充失效問題解決:
把date 改爲了LocalDateTime 類型,依然對應的是SQL中的datetiem或date類型。
結果:
@Test
public void testInsert(){
User user =new User();
user.setAge(23);
int result = userMapper.insert(user); //自動生成id
System.out.println(result);
}
當執行插入或者更新操作時候,自動修改值。
5. 樂觀鎖:
顧名思義十分樂觀,它總是認爲程序不會出現問題,無論幹什麼都不去上鎖!如果出了問題,就在測試加鎖處理,再次更新值測試。
顧名思義十分悲觀,它總是認爲程序會出現問題,無論幹什麼都去上鎖!再去操作。
樂觀鎖原理機制:
樂觀鎖實現方式:
- 後端取記錄數據時,先查詢獲取當前version版本。
- 更新時,帶上這個version
- 執行更新時,set version = newVersion where version = oldVersion
- 如果version不對,就不允許更新,失敗操作。
主要適用場景:當要更新一條記錄的時候,希望這條記錄沒有被別人更新。
5.1實現過程:
-
在數據庫表中添加版本字段,version int ,設置默認值1.
-
實體類添加版本註解:
@Version //樂觀鎖註解 private Integer version;
註解 @Version 必須有!
-
自定義一個配置類mpConfig:
標識爲mybatis配置類,註冊樂觀鎖插件即可,源碼如下:
package com.zout.mapper; /** * @Description: * @Author: Zoutao * @Date: 2020/5/16 */ import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement @Configuration //標示爲mybatis配置類 public class mpConfig { // 註冊樂觀鎖插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
如圖:
- 測試方法:
// 測試樂觀鎖 >>單線程時,方法可行
@Test
public void testInterceptor(){
// 1.查詢用戶信息
User user = userMapper.selectById(4L);
// 2.修改用戶信息
user.setName("王五");
user.setAge(500);
user.setEmail("[email protected]");
// 3. 執行更新操作
int result = userMapper.updateById(user);
System.out.println(result);
}
過程剖析:
首先查詢version=1,然後更新以後,version=2了。
單線程下:
對應SQL原理
update user set name = '王五',version = 2 where id = 100 and version = 1
結果,版本正常,更新成功。
多線程下:
// 測試樂觀鎖 >> 多線程下,發現方法不可行
@Test
public void testInterceptor2(){
// 模擬線程1
User user1 = userMapper.selectById(4L);
user1.setName("陳二");
user1.setAge(500);
// 模擬線程2,執行插隊操作。跟線程1互相搶奪
User user2 = userMapper.selectById(4L);
user2.setName("王二麻子");
user2.setAge(800);
userMapper.updateById(user2); // 模擬線程2搶先操作
// 執行更新操作
userMapper.updateById(user1); //如果沒有樂觀鎖,就會覆蓋插隊線程2的值。有樂觀鎖,則更新失敗。
}
發現兩個線程都拿到了版本2,加鎖。
最後,因爲我們有樂觀鎖存在,所以線程2的更新操作成功了,而線程1的更新則失敗了,被限制。
可以嘗試自旋鎖來嘗試多次提交,依然可以避免同時被改寫的問題。
6. 邏輯刪除
- 物理刪除:從數據庫直接刪除數據。
- :在數據庫中沒有直接刪除,而是通過一個變量讓它失效,依舊保存在數據庫中,只是不顯示了。
應用場景:
- 管理員可以查看被刪除的記錄,普通用戶刪除後,數據消失!這樣做是防止數據的丟失,類似於回收站功能。
6.1實現過程:
-
比如在數據庫表中新增一個字段 deleted。
當(deleted=0 >> deleted=1 )時,表示該數據被刪除了。 -
實體類字段上加上@TableLogic註解
@TableLogic private Integer deleted;
說明:
• 字段支持所有數據類型(推薦使用 Integer,Boolean,LocalDateTime)
• 如果使用LocalDateTime,建議邏輯未刪除值設置爲字符串null,邏輯刪除值只支持數據庫函數例如now()
-
全局配置-全局邏輯刪除(非必須)
mp,3.1.1版本以上使用,0未刪除,1已刪除。#全局邏輯刪除字段值 mybatis-plus.global-config.db-config.logic-delete-field: flag # 邏輯已刪除值(默認爲 1) mybatis-plus.global-config.db-config.logic-delete-value: 1 # 邏輯未刪除值(默認爲 0) mybatis-plus.global-config.db-config.logic-not-delete-value: 0
如圖:
注意:
使用此配置則不需要在實體類上添加 @TableLogic。
但如果實體類上有 @TableLogic 則以實體上的爲準,忽略這個全局配置。( 即先查找註解再查找全局,都沒有則此表沒有邏輯刪除。)
- 測試方法:
//刪除-邏輯刪除
@Test
public void testDelById(){
userMapper.deleteById(5L);
}
原理:
效果:
使用mp自帶方法刪除和查找都會附帶邏輯刪除功能 (自己寫的xml不會)
刪除:update user set deleted=1 where id =1 and deleted=0
查找:select * from user where deleted=0
再查詢數據時:會自動加入邏輯未刪除的標誌語句來查。(自己寫的SQL則加上這個條件即可。)
如圖:
記錄依舊存在數據庫,但是值已經改變了,所以普通用戶查尋不到,admin可以。
7. 性能分析-執行 SQL 分析打印
在平時的開發中,會遇到一些慢SQL,一般是進行測試,druid監控等來找誰慢。MP提供自有插件來分析慢SQL ,如果超過設定時間,就停止運行。
(該插件 MP3.2.0 以上版本移除推薦使用第三方擴展p6spy ,執行SQL分析打印功能)
該功能依賴 p6spy 組件,完美的輸出打印 SQL 及執行時長 ,MP 3.1.0 以上版本使用。
p6spy 依賴引入:
<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.8.7</version>
</dependency>
作用:用於輸出每條 SQL 語句及其執行時間。
效果:SQL會格式化,且出現執行時間,如果超過指定時間,會提醒報錯,自己修改優化SQL即可。
注意:該插件有性能損耗,不建議生產環境使用,在springboot配置環境爲dev或者test環境下使用就好了。
以上爲使用 mybatis Plus 框架時的一些開發記錄,也適用於以後的項目當中。