springboot2.3 整合mybatis-plus 高級(jí)功能(圖文詳解)
—學(xué)習(xí)并使用mybatis-plus的一些高級(jí)功能的用法例如: AR模式、 樂(lè)觀鎖 、邏輯刪除 、自動(dòng)填充、數(shù)據(jù)保護(hù)等功能
為了方便演示,咱們還是新建一個(gè)全新的項(xiàng)目
引入mp依賴
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency>
yml配置
# 配置端口server: port: 8080spring: # 配置數(shù)據(jù)源 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 username: root password: root# mybatis-plus相關(guān)配置mybatis-plus: # 以下配置均有默認(rèn)值,可以不設(shè)置 global-config: db-config: #主鍵類型 auto:'數(shù)據(jù)庫(kù)ID自增' id-type: auto configuration: # 是否開(kāi)啟自動(dòng)駝峰命名規(guī)則映射:從數(shù)據(jù)庫(kù)列名到Java屬性駝峰命名的類似映射 map-underscore-to-camel-case: true # 如果查詢結(jié)果中包含空值的列,則 MyBatis 在映射的時(shí)候,不會(huì)映射這個(gè)字段 call-setters-on-nulls: true # 這個(gè)配置會(huì)將執(zhí)行的sql打印出來(lái),在開(kāi)發(fā)或測(cè)試的時(shí)候可以用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
在 Spring Boot 啟動(dòng)類中添加 @MapperScan 注解,掃描 Mapper 文件夾
(一)AR模式使用
ActiveRecord模式:支持 ActiveRecord 形式調(diào)用,實(shí)體類只需繼承 Model 類即可進(jìn)行強(qiáng)大的 CRUD 操作
即直接使用實(shí)體類 CRUD操作
1.繼承Model
點(diǎn)進(jìn)Model 類中 ,發(fā)現(xiàn)其提供了一些基礎(chǔ)的CRUD操作方法,并實(shí)現(xiàn)了序列化接口
注意的是,如果要使用ActiveRecord模式,僅僅繼承Model 是不行的,這一點(diǎn)官網(wǎng)沒(méi)有很顯示的提到,還需要編寫mapper接口 繼承BaseMapper接口,泛型為當(dāng)前實(shí)體類
2.mapper接口
如果不編寫Mapper直接使用Model方法會(huì)報(bào)錯(cuò):
那么,咱們來(lái)編寫mapper
再次測(cè)試
3.CRUD操作
新增操作
查詢操作
修改操作
刪除操作
此AR模式呢,可以在開(kāi)發(fā)中減少我們很多的調(diào)用代碼,簡(jiǎn)單操作無(wú)需額外使用Mapper、service調(diào)用!
(二)樂(lè)觀鎖
使用Mybatis-plus實(shí)現(xiàn)樂(lè)觀鎖
樂(lè)觀鎖:總是假設(shè)最好的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù),樂(lè)觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量。
mybatis-plus 提供的樂(lè)觀鎖 是采用了版本號(hào)機(jī)制
數(shù)據(jù)表中加上一個(gè)數(shù)據(jù)版本號(hào)version字段,表示數(shù)據(jù)被修改的次數(shù),當(dāng)數(shù)據(jù)被修改時(shí),version值會(huì)加一。當(dāng)線程A要更新數(shù)據(jù)值時(shí),在讀取數(shù)據(jù)的同時(shí)也會(huì)讀取version值,在提交更新時(shí),若剛才讀取到的version值為當(dāng)前數(shù)據(jù)庫(kù)中的version值相等時(shí)才更新,否則重試更新操作,直到更新成功。
mp項(xiàng)目使用樂(lè)觀鎖是使用其插件配置?配置Bean的形式
1.bean配置
@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor();}
2.實(shí)體類以及數(shù)據(jù)庫(kù)添加對(duì)應(yīng)字段以及列
@Versionprivate Integer version;
至于version初始值,可以數(shù)據(jù)庫(kù)默認(rèn)設(shè)置為0,也可以再新增數(shù)據(jù)時(shí)手動(dòng)設(shè)置verion版本號(hào),個(gè)人是采用了數(shù)據(jù)庫(kù)默認(rèn)值
那么我們?cè)谧鋈魏尾樵冃薷膭h除的時(shí)候呢,mp會(huì)默認(rèn)幫我們吧版本號(hào)作為條件帶上,判斷與數(shù)據(jù)庫(kù)中該數(shù)據(jù)版本號(hào)是否一致。
3.相關(guān)操作以及注意事項(xiàng)
首先插入一條數(shù)據(jù)
查詢并修改該條信息
發(fā)現(xiàn)其在修改的時(shí)候,將version作為了條件,并對(duì)version做了修改 set操作(默認(rèn)是在原基礎(chǔ)上+1)
需要注意的幾個(gè)點(diǎn):
支持的數(shù)據(jù)類型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime整數(shù)類型下 newVersion = oldVersion + 1newVersion 會(huì)回寫到 entity 中僅支持 updateById(id) 與 update(entity, wrapper) 方法在 update(entity, wrapper) 方法下, wrapper 不能復(fù)用!!!
什么意思呢,就是要想使用樂(lè)觀說(shuō),首先 類型有限制,其次,修改方法僅僅只有上方兩個(gè)有效,才會(huì)在修改時(shí)候?qū)Π姹咎?hào)就行修改操作 以及 要想版本號(hào)升級(jí)必須 是要把之前版本號(hào)傳過(guò)去。
即version操作時(shí),我必須把舊的帶過(guò)去
版本號(hào)不對(duì),是無(wú)法進(jìn)行數(shù)據(jù)更新操作的(刪除、修改)
而如果不傳遞版本號(hào)的話,那么該字段就不會(huì)被維護(hù)了(失去了樂(lè)觀鎖 版本號(hào)機(jī)制 的意義了)
版本號(hào)回傳 修改正確 版本號(hào)維護(hù)成功
如此,就實(shí)現(xiàn)了樂(lè)觀鎖了,mp基礎(chǔ)上使用樂(lè)觀鎖,就是這么簡(jiǎn)單
(三)邏輯刪除
物理刪除:將數(shù)據(jù)庫(kù)中該信息進(jìn)行徹底刪除,無(wú)法恢復(fù)。 應(yīng)的SQL語(yǔ)句:delete from 表名 where 條件
邏輯刪除:邏輯刪除的本質(zhì)是修改操作,所謂的邏輯刪除其實(shí)并不是真正的刪除,而是在表中將對(duì)應(yīng)的是否刪除標(biāo)識(shí),然后修改 查詢操作時(shí)將 是否刪除標(biāo)識(shí)字段作為條件帶上,進(jìn)而達(dá)到邏輯上刪除了數(shù)據(jù),但實(shí)際仍然保留了數(shù)據(jù)(盡管此數(shù)據(jù)在邏輯中不會(huì)再使用了)
例如:自己設(shè)置一個(gè)邏輯刪除字段,例如1表示數(shù)據(jù)有效,0表示被刪除即可,默認(rèn)是1
Mybatis-plus 已經(jīng)為我們提供了邏輯刪除封裝
只對(duì)自動(dòng)注入的sql起效:
插入: 不作限制查找: 追加where條件過(guò)濾掉已刪除數(shù)據(jù),且使用 wrapper.entity 生成的where條件會(huì)忽略該字段更新: 追加where條件防止更新到已刪除數(shù)據(jù),且使用 wrapper.entity 生成的where條件會(huì)忽略該字段刪除: 轉(zhuǎn)變?yōu)?更新例如:
刪除: update user set deleted=1 where id = 1 and deleted=0查找: select id,name,deleted from user where deleted=0字段類型支持說(shuō)明:
支持所有數(shù)據(jù)類型(推薦使用 Integer,Boolean,LocalDateTime)如果數(shù)據(jù)庫(kù)字段使用datetime,邏輯未刪除值和已刪除值支持配置為字符串null,另一個(gè)值支持配置為函數(shù)來(lái)獲取值如now()附錄:
邏輯刪除是為了方便數(shù)據(jù)恢復(fù)和保護(hù)數(shù)據(jù)本身價(jià)值等等的一種方案,但實(shí)際就是刪除。如果你需要頻繁查出來(lái)看就不應(yīng)使用邏輯刪除,而是以一個(gè)狀態(tài)去表示。
使用步驟:
1.數(shù)據(jù)庫(kù)添加字段
對(duì)某個(gè)表的數(shù)據(jù)需要使用邏輯刪除,則必須創(chuàng)建邏輯刪除字段
2.mp邏輯刪除配置
在原有的mp配置上 添加邏輯刪除設(shè)置
其已刪除 未刪除值均有默認(rèn)值 1 0 如果你預(yù)想設(shè)置值與之一致,僅僅編寫 邏輯刪除字段名即可
3.實(shí)體類加邏輯刪除字段以及注解
mp中 邏輯刪除相關(guān)配置就沒(méi)了 ,很是簡(jiǎn)單------------咱們開(kāi)始測(cè)試
咱們先刪除數(shù)據(jù)庫(kù)中原有數(shù)據(jù)
可以看到 ,刪除語(yǔ)句實(shí)際上是執(zhí)行了update(修改) 額外將 我們?cè)O(shè)置好的邏輯刪除字段值作為條件(未刪除 0),并修改 值為1(已刪除)
接下來(lái),我們來(lái)進(jìn)行查詢操作,看是否還能取出數(shù)據(jù)
total=0 ,沒(méi)有數(shù)據(jù), 那么我們便做到了業(yè)務(wù)意義上的數(shù)據(jù)刪除(邏輯刪除)
其只要是設(shè)置了邏輯刪除 ,修改 刪除 查詢操作均會(huì)攜帶上未刪除條件 ,保證操作的數(shù)據(jù)時(shí) “存在” 的()
(四)自動(dòng)填充
在開(kāi)發(fā)中哈,我們一個(gè)表的字段除了業(yè)務(wù)字段外,實(shí)際上還會(huì)添加維護(hù)字段 ,即 創(chuàng)建人 創(chuàng)建時(shí)間 修改人 修改時(shí)間 ,讓數(shù)據(jù)的操作 有跡可循,方便管理
如下:
實(shí)體類添加維護(hù)字段信息
那么每次在操作數(shù)據(jù)的時(shí)候需要我們手動(dòng)插入到該model中 進(jìn)行保存,再操作數(shù)據(jù)
例如添加時(shí)
例如修改時(shí)
這樣呢,每次我們?cè)诓僮鲾?shù)據(jù)時(shí)都需要在業(yè)務(wù)代碼中編寫 新增用戶ID 當(dāng)前時(shí)間 修改用戶ID 修改時(shí)間等字段等,就比較繁瑣
項(xiàng)目很大,幾百個(gè)Model 乘以Four 那么就額外編寫幾千行代碼進(jìn)行維護(hù)了
解決辦法:
1.自定義Aop 進(jìn)行數(shù)據(jù)填充
2.使用現(xiàn)成的 (mp都用了,用他的自動(dòng)填充不香嗎)
操作開(kāi)始:
1.自定義實(shí)現(xiàn)類 MyMetaObjectHandler
這里需要注意哈
這二者呢,一個(gè)是插入的類型 一個(gè)是插入的值 必須對(duì)應(yīng)上,或者 自動(dòng)填充時(shí)會(huì)報(bào)錯(cuò)
@Slf4j@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { log.info('start insert fill ....'); this.strictInsertFill(metaObject, 'createTime', Long.class, System.currentTimeMillis()); //開(kāi)發(fā)中 創(chuàng)建者 修改者ID 通過(guò)Aop 或者權(quán)限框架獲取當(dāng)前操作用戶 ,用用戶ID進(jìn)行填充即可 this.strictInsertFill(metaObject, 'createId', Long.class, 666L); } @Override public void updateFill(MetaObject metaObject) { log.info('start update fill ....'); this.strictUpdateFill(metaObject, 'modifyTime', Long.class, System.currentTimeMillis()); this.strictUpdateFill(metaObject, 'modifyId', Long.class, 666L); }}
2.實(shí)體類注解
要開(kāi)啟自動(dòng)填充功能 實(shí)現(xiàn) MetaObjectHandler 還不行,因?yàn)镸p 啊,對(duì)其字段默認(rèn)是不做處理的,所以,咱們配置了 新增修改 自動(dòng)填充時(shí) 還需要在其字段上進(jìn)行開(kāi)啟
@TableField(fill = FieldFill.INSERT)
OK。設(shè)置好了,咱們插入修改試一試吧!
修改
如此 mp的自動(dòng)填充就可以了,根據(jù)業(yè)務(wù) 合理對(duì)默寫字段填充,填充什么值 自己考量即可
但是呢,也是有一定問(wèn)題的
比如:我有一個(gè)定時(shí)維護(hù)功能,是由xxl-job發(fā)起的定時(shí)任務(wù),那么這個(gè)時(shí)候,我字段維護(hù)信息,為了區(qū)分開(kāi)到底是認(rèn)為維護(hù)還是自動(dòng)維護(hù),那么就需要手動(dòng)設(shè)置 創(chuàng)建修改人 以及時(shí)間了
這個(gè)時(shí)候呢,自動(dòng)填充功能會(huì)把我們自己設(shè)置的進(jìn)行覆蓋
所以呢,自動(dòng)填充咱們也可以進(jìn)行優(yōu)化,當(dāng)有該維護(hù)字段時(shí),且字段無(wú)值時(shí)才填充,當(dāng)無(wú)維護(hù)字段或者 維護(hù)字段設(shè)置了值則不填充
3.自動(dòng)填充優(yōu)化
實(shí)際就是做了判斷 判斷該字段是否有set屬性 有則嘗試填充 (無(wú)值則填充)
@Slf4j@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { //判斷是否有set屬性 if (metaObject.hasSetter('createId')) { //判斷是否有值 如果沒(méi)設(shè)置值 則為null Object createId = getFieldValByName('createId', metaObject); if (createId == null) { log.info('創(chuàng)建人需要維護(hù) 且值為空 需要填充 '); //開(kāi)發(fā)中 創(chuàng)建者 修改者ID 通過(guò)Aop 或者權(quán)限框架獲取當(dāng)前操作用戶 ,用用戶ID進(jìn)行填充即可 this.strictInsertFill(metaObject, 'createId', Long.class, 666L); } } if (metaObject.hasSetter('createTime')) { Object createTime = getFieldValByName('createTime', metaObject); if (createTime == null) { log.info('創(chuàng)建時(shí)間需要維護(hù) 且值為空 需要填充 '); this.strictInsertFill(metaObject, 'createTime', Long.class, System.currentTimeMillis()); } } } @Override public void updateFill(MetaObject metaObject) { if (metaObject.hasSetter('modifyId')) { Object modifyId = getFieldValByName('modifyId', metaObject); if (modifyId == null) { log.info('修改人需要維護(hù) 且值為空 需要填充 '); this.strictUpdateFill(metaObject, 'modifyId', Long.class, 666L); } } if (metaObject.hasSetter('modifyTime')) { Object modifyTime = getFieldValByName('modifyTime', metaObject); if (modifyTime == null) { log.info('修改時(shí)間需要維護(hù) 且值為空 需要填充 '); this.strictUpdateFill(metaObject, 'modifyTime', Long.class, System.currentTimeMillis()); } } }}
測(cè)試:
我這里設(shè)置了創(chuàng)建者ID ,雖然createId需要維護(hù),但我們?cè)O(shè)置了值 則不會(huì)進(jìn)行填充了,修改亦是如此!
那么自動(dòng)填充功能,就完了,整合時(shí) 結(jié)合項(xiàng)目,適當(dāng)修改即可。
(五)數(shù)據(jù)保護(hù)
使用mp中的加密方式,對(duì)配置文件中一些敏感信息進(jìn)行加密(例如密碼)
其和 Jasypt 項(xiàng)目加密方式類似
1.生成隨機(jī)秘鑰以及內(nèi)容加密
2.配置文件替換
我們將原本的明文信息(root)換成加密后的密文信息
格式:
mpw:xxxx密文信息
3.設(shè)置密鑰到啟動(dòng)參數(shù)中
idea 設(shè)置方式:
啟動(dòng)測(cè)試?獲取到了數(shù)據(jù) 證明數(shù)據(jù)庫(kù)連接成功,其加密內(nèi)容在連接時(shí)也被密鑰解密了,但配置中,無(wú)明文信息,一定程度保護(hù)了項(xiàng)目
jar 啟動(dòng)時(shí) 設(shè)置密鑰參數(shù):
項(xiàng)目地址:springboot-mp進(jìn)階
到此這篇關(guān)于springboot2.3 整合mybatis-plus 高級(jí)功能的文章就介紹到這了,更多相關(guān)springboot2.3 整合mybatis-plus內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. js select支持手動(dòng)輸入功能實(shí)現(xiàn)代碼2. asp.net core項(xiàng)目授權(quán)流程詳解3. CSS3中Transition屬性詳解以及示例分享4. jsp文件下載功能實(shí)現(xiàn)代碼5. 開(kāi)發(fā)效率翻倍的Web API使用技巧6. bootstrap select2 動(dòng)態(tài)從后臺(tái)Ajax動(dòng)態(tài)獲取數(shù)據(jù)的代碼7. vue使用moment如何將時(shí)間戳轉(zhuǎn)為標(biāo)準(zhǔn)日期時(shí)間格式8. html中的form不提交(排除)某些input 原創(chuàng)9. PHP橋接模式Bridge Pattern的優(yōu)點(diǎn)與實(shí)現(xiàn)過(guò)程10. ASP常用日期格式化函數(shù) FormatDate()
