MybatisPlus高級特性
- 2022 年 8 月 9 日
- 筆記
- mybatis, mybatisPlus
MybatisPlus高級特性
1. 公共欄位自動填充
1.1 問題分析
在新增員工時需要設置
創建時間、創建人、修改時間、修改人等欄位
,在編輯員工時需要設置修改時間、修改人等欄位
。這些欄位屬於公共欄位
,也就是也就是在我們的系統中很多表中都會有這些欄位,如下:
而針對於這些欄位,我們的賦值方式為:
A. 在新增數據時, 將createTime、updateTime 設置為當前時間, createUser、updateUser設置為當前登錄用戶ID。
B. 在更新數據時, 將updateTime 設置為當前時間, updateUser設置為當前登錄用戶ID。
1.2 基本功能實現
1.2.1 思路分析
Mybatis Plus公共欄位自動填充,也就是在插入或者更新的時候為指定欄位賦予指定的值,使用它的好處就是可以統一對這些欄位進行處理,避免了
重複程式碼
。在上述的問題分析中,我們提到有四個公共欄位
,需要在新增/更新中進行賦值操作, 具體情況如下:
欄位名 | 賦值時機 | 說明 |
---|---|---|
createTime | 插入(INSERT) | 當前時間 |
updateTime | 插入(INSERT) , 更新(UPDATE) | 當前時間 |
createUser | 插入(INSERT) | 當前登錄用戶ID |
updateUser | 插入(INSERT) , 更新(UPDATE) | 當前登錄用戶ID |
實現步驟:
1、在實體類的屬性上加入
@TableField註解
,指定自動填充的策略。2、按照框架要求編寫
元數據對象處理器
,在此類中統一為公共欄位賦值
,此類需要實現MetaObjectHandler介面
。
1.2.2 程式碼實現
1). 實體類的屬性上加入@TableField註解,指定自動填充的策略。
這裡就不提供程式碼,要注
創建時間和創建人
只在insert語句中需要自動填充。
- FieldFill.INSERT: 插入時填充該屬性值
- FieldFill.INSERT_UPDATE: 插入/更新時填充該屬性值
2). 按照框架要求編寫元數據對象處理器,在此類中統一為公共欄位賦值,此類需要實現MetaObjectHandler介面。
放在項目的common包下
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 自定義元數據對象處理器
*/
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
/**
* 插入操作,自動填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共欄位自動填充[insert]...");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
metaObject.setValue("createUser",new Long(1));
metaObject.setValue("updateUser",new Long(1));
}
/**
* 更新操作,自動填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共欄位自動填充[update]...");
log.info(metaObject.toString());
metaObject.setValue("updateTime",LocalDateTime.now());
metaObject.setValue("updateUser",new Long(1));
}
}
1.3 功能完善
1.3.1 思路分析
前面我們已經完成了公共欄位自動填充功能的程式碼開發,但是還有一個問題沒有解決,就是我們在自動填充createUser和updateUser時設置的
用戶id是固定值
,現在我們需要完善,改造成動態獲取當前登錄用戶的id
。大家可能想到,用戶登錄成功後我們將用戶id存入了HttpSession中,現在我從HttpSession中獲取不就行了?
注意,我們在MyMetaObjectHandler類中是不能直接獲得HttpSession對象的,所以我們需要通過其他方式來獲取登錄用戶id。
- 那麼我們先搞清楚一點,當我們在修改員工資訊時, 我們業務的執行流程是什麼樣子的,如下圖:
1.3.2 ThreadLocal
ThreadLocal並不是一個Thread,而是
Thread的局部變數
。當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。ThreadLocal為每個執行緒提供單獨一份存儲空間,具有執行緒隔離的效果,只有在
執行緒內才能獲取到對應的值
,執行緒外則不能訪問當前執行緒對應的值。
ThreadLocal常用方法:
A. public void set(T value) : 設置當前執行緒的執行緒局部變數的值
B. public T get() : 返回當前執行緒所對應的執行緒局部變數的值
C. public void remove() : 刪除當前執行緒所對應的執行緒局部變數的值
我們可以在LoginCheckFilter(過濾器)的doFilter方法中獲取當前登錄用戶id,並調用ThreadLocal的set方法來設置當前執行緒的執行緒局部變數的值(用戶id),然後在MyMetaObjectHandler的updateFill方法中調用ThreadLocal的get方法來獲得當前執行緒所對應的執行緒局部變數的值(用戶id)。 如果在後續的操作中, 我們需要在Controller / Service中要使用當前登錄用戶的ID, 可以直接從ThreadLocal直接獲取。
1.3.3 操作步驟
實現步驟:
1). 編寫UserThreadLocal工具類,基於ThreadLocal封裝的工具類
2). 在LoginCheckFilter的doFilter方法中調用BaseContext來設置當前登錄用戶的id
3). 在MyMetaObjectHandler的方法中調用BaseContext獲取登錄用戶的id
1.3.4 程式碼實現
1). UserThreadLocal工具類
/**
* 基於ThreadLocal封裝工具類,用戶保存和獲取當前登錄用戶id
*/
public class UserThreadLocal {
private UserThreadLocal() {
}
private static ThreadLocal<Long> THREADLOCAL = new ThreadLocal<>();
/**
* 設置值
*
* @param id
*/
public static void setCurrentId(Long id) {
THREADLOCAL.set(id);
}
/**
* 獲取值
*
* @return
*/
public static Long getCurrentId() {
return THREADLOCAL.get();
}
public static void remove() {
THREADLOCAL.remove();
}
}
2).LoginCheckFilter中存放當前登錄用戶到ThreadLocal
在doFilter方法中, 判定用戶是否登錄, 如果用戶登錄, 在放行之前, 獲取HttpSession中的登錄用戶資訊, 調用BaseContext的setCurrentId方法將當前登錄用戶ID存入ThreadLocal。
有些小夥伴肯定會有疑問,清除id的方法就寫在下面,這不就等於沒設嗎,方法都沒走完就給清除了。
filterChain.doFilter(request, response);
會等待 Controller,等一系類方法的調用,才會結束
。我解釋的不是很完美,大家可以自行測試
3). MyMetaObjectHandler中從ThreadLocal中獲取
將之前在程式碼中固定的當前登錄用戶1, 修改為動態調用UserThreadLocal中的getCurrentId方法獲取當前登錄用戶ID
1.3.5 功能測試
完善了元數據對象處理器之後,我們就可以重新啟動項目,對插入或者更新的介面去測試。
2. 邏輯刪除
2.1 問題分析
在實際的項目中,數據是十分寶貴的,所以不會做到真正的去刪除。資料庫中一般會存在如下欄位:
2.2 思路分析
MybatisPlus為我們提供了許多種的配置方法。
- 可以在yaml中配置全局的邏輯刪除
- 也可以在每個實體類中
2.3 程式碼實現
2.3.1配置全局
配置yaml
- 圖中紅框中的就是全局邏輯刪除的配置,其他的可以根據需要自行添加
logic-delete-field: deleted
配置的一定對應上實體類的變數名稱@TableField(value = "is_deleted")
value對應的是資料庫的欄位
mybatis-plus:
configuration:
#在映射實體或者屬性時,將資料庫中表名和欄位名中的下劃線去掉,按照駝峰命名法映射 address_book ---> AddressBook
map-underscore-to-camel-case: true
#日誌輸出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: ASSIGN_ID # 全局配置資料庫id生成的策略
logic-delete-field: deleted # 全局邏輯刪除的實體欄位名(實體類)
logic-delete-value: 1 # 邏輯已刪除值(默認為1)
logic-not-delete-value: 0 # 邏輯未刪除值(默認為0)
2.3.2實體類中
@TableLogic(value = "0", delval = "1")
配置邏輯刪除欄位的值,value
的值表示未刪除的時候的值,delval
的值表示已刪除時候的值;