SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解
一.字段自動(dòng)填充引入
先看一個(gè)現(xiàn)象,在之前寫好的表中,我們發(fā)現(xiàn)有很多字段重復(fù)出現(xiàn)
比如update_time、create_time、create_user…
這就導(dǎo)致需要在Controller層中每一次對(duì)表中數(shù)據(jù)進(jìn)行修改后調(diào)用一次.setCreateTime(LocalDateTime.now());或者setUpdateTime(LocalDateTime.now());等等
“硬編碼問題又出現(xiàn)了”顯得格外麻煩
這些共性字段如何統(tǒng)一拿出來處理呢?MyBatisPlus給了我們解決方案,為了實(shí)現(xiàn)這一功能:
首先
我們需要在公共字段對(duì)應(yīng)的實(shí)體屬性上加上@TableField注解與指定填充策略,就像這樣:
@TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT) private Long createUser; @TableField(fill = FieldFill.INSERT_UPDATE) private Long updateUser;
不難看出,fill的類型是一個(gè)枚舉類
若是不需要處理則選擇DEAFULT,若是增添后需要修改字段的值選擇INSERT,若是涉及修改數(shù)據(jù)時(shí)需要修改字段的值則選擇UPDATE,若是插入和修改都需要設(shè)置字段的值則選擇INSERT_UPDATE就像updateTime和updateUser,只要涉及對(duì)數(shù)據(jù)的操作就要修改字段的值
類似于全局異常處理器,為了實(shí)現(xiàn)字段填充也需要定義一個(gè)元數(shù)據(jù)對(duì)象處理器
二.元數(shù)據(jù)對(duì)象處理器
在定義的類中實(shí)現(xiàn)MetaObjecthandler接口,并重寫接口中策略對(duì)應(yīng)的方法,就像這樣:
@Component @Slf4j public class MyMetaObjecthandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { log.info("公共字段自動(dòng)填充..."); log.info(metaObject.toString()); } @Override public void updateFill(MetaObject metaObject) { log.info("公共字段自動(dòng)填充..."); log.info(metaObject.toString()); } }
這里有點(diǎn)像繼承HttpServlet那個(gè)套路,重寫doGet()與doPost()方法,要實(shí)現(xiàn)什么功能就寫在對(duì)應(yīng)的方法里…
重寫完方法,不妨debug一下:
發(fā)現(xiàn)我實(shí)現(xiàn)接口后在方法里拿到了實(shí)體的數(shù)據(jù),并封裝到了形參里的metaObject對(duì)象中,接下來我就可以利用此對(duì)象來做公共字段的集中處理了!
所謂的自動(dòng)填充也正是因?yàn)樵擃悡碛蠤Component注解,在每一次的項(xiàng)目啟動(dòng)后就會(huì)被掃描到,加載到,而類中的方法又實(shí)現(xiàn)了功能,所以才可以做到自動(dòng)填充字段!
回到正題
我們要做的是把公共的字段做到統(tǒng)一填充,具體實(shí)現(xiàn)則是在重寫的方法里調(diào)用setValue()方法并傳進(jìn)去字段與參數(shù),就像這樣:
@Override public void insertFill(MetaObject metaObject) { log.info("公共字段自動(dòng)填充..."); log.info(metaObject.toString()); metaObject.setValue("createTime", LocalDateTime.now()); metaObject.setValue("updateTime",LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { log.info("公共字段自動(dòng)填充..."); log.info(metaObject.toString()); metaObject.setValue("updateTime",LocalDateTime.now()); }
相比之前controller層中的employee.setCreateTime(LocalDateTime.now());/employee.setUpdateTime(LocalDateTime.now());
來看還是比較好理解,無(wú)非就是“茴“字的另一種寫法
(但卻實(shí)現(xiàn)了一勞永逸)…
可能有人會(huì)問,懶羊羊你的updateUser與createUser字段怎么不處理呢?
確實(shí),還記得之前在controller層中我們是怎么處理的嗎?
為了確定User是誰(shuí),我們從Session里拿到了操作的對(duì)象id,并調(diào)用set方法修改了對(duì)象的字段值
按照上面的經(jīng)驗(yàn),為了能夠動(dòng)態(tài)的拿到id,我們應(yīng)該這樣設(shè)置:
可是,在此方法中metaObject對(duì)象好像不能利用Session里的empID,那要如何獲得對(duì)象的標(biāo)識(shí)呢?
不就是一個(gè)標(biāo)識(shí)么,我用線程id可以嗎?
三.Threadlocal的使用
在這之前,需要明白一點(diǎn),每當(dāng)客戶端發(fā)送一次HTTP請(qǐng)求,對(duì)應(yīng)在服務(wù)器端會(huì)分配一個(gè)新的線程來處理。項(xiàng)目設(shè)計(jì)到現(xiàn)在為止,以點(diǎn)擊一次保存按鈕作為一次請(qǐng)求,它會(huì)觸發(fā)過濾器、調(diào)用Controller層、MetaObjectHandler層的方法
我們通過Thread的內(nèi)部方法:long id = Thread.currentThread().getId();
來獲得當(dāng)前線程的id,以日志的形式log.info("當(dāng)前線程id:{}",id);
輸出到控制臺(tái):
可見,三者的線程id相同,說明他們?cè)谕粋€(gè)線程中,這就保證了一致性,也正是因?yàn)檫@個(gè)特性,所以可用來當(dāng)作表中的字段id使用來作為標(biāo)識(shí)
在此基礎(chǔ)上,大致方向已經(jīng)敲定是利用線程的特性,具體要如何實(shí)現(xiàn)呢?那就不得不需要了解一下ThreadLocal這個(gè)概念了:
1.ThreadLocal并不是一個(gè)Thread,而是Thread的局部變量。
2.當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。
3.ThreadLocal為每個(gè)線程提供單獨(dú)一份存儲(chǔ)空間,具有線程隔離的效果,只有在線程內(nèi)才能獲取到對(duì)應(yīng)的值,線程外則不能訪問
常用方法
public void set(T value) | 設(shè)置當(dāng)前線程局部變量的值 |
---|---|
public T get() | 返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量的值 |
了解到ThreadLocal特性,我們就可以結(jié)合“同一線程”這一特性來獲得那個(gè)對(duì)象唯一的SessionId。
?? 在前面的登錄功能中我們?cè)O(shè)置了一個(gè)filter,在filter中我們已經(jīng)拿到過了一次SessionId,寫到這里解決方案不就出來了嘛——把filter中的SessionId當(dāng)作參數(shù)傳給ThreadLocal的set方法,然后在MetaObjectHandler實(shí)現(xiàn)類(元數(shù)據(jù)對(duì)象處理器)中調(diào)用ThreadLocal的get()方法得到SessionId
就像這樣:
1.為了規(guī)范,我們封裝一個(gè)類,功能是調(diào)用ThreadLocal的方法
/** * 基于ThreadLocal封裝工具類,用于保護(hù)和獲取當(dāng)前登錄id */ public class BaseContext { private static ThreadLocal<Long> threadLocal=new ThreadLocal(); public static void setCurrentId(Long currentId){ threadLocal.set(currentId); } public static Long getCurrentId(){ return threadLocal.get(); } }
2.在filter中做到SessionId的遷移
3.在MetaObjectHandler實(shí)現(xiàn)類中利用SessionId完成公共字段填充設(shè)置
@Override public void updateFill(MetaObject metaObject) { log.info("公共字段自動(dòng)填充..."); log.info(metaObject.toString()); long id = Thread.currentThread().getId(); log.info("當(dāng)前線程id:{}",id); metaObject.setValue("updateTime",LocalDateTime.now()); metaObject.setValue("createUser",BaseContext.getCurrentId()); metaObject.setValue("updateUser",BaseContext.getCurrentId()); }
這樣,就可以一勞永逸咯~
又是一個(gè)小技巧?。?/p>
到此這篇關(guān)于SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解的文章就介紹到這了,更多相關(guān)SpringBoot自動(dòng)填充內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot在filter中如何用threadlocal存放用戶身份信息
- SpringBoot中的ThreadLocal保存請(qǐng)求用戶信息的實(shí)例demo
- springboot登錄攔截器+ThreadLocal實(shí)現(xiàn)用戶信息存儲(chǔ)的實(shí)例代碼
- SpringBoot ThreadLocal 簡(jiǎn)單介紹及使用詳解
- SpringBoot+ThreadLocal+AbstractRoutingDataSource實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源
- Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案
- SpringBoot通過ThreadLocal實(shí)現(xiàn)登錄攔截詳解流程
- springboot 使用ThreadLocal的實(shí)例代碼
- SpringBoot中使用?ThreadLocal?進(jìn)行多線程上下文管理及注意事項(xiàng)小結(jié)
相關(guān)文章
SpringCloud_Sleuth分布式鏈路請(qǐng)求跟蹤的示例代碼
Spring Cloud Sleuth是一款針對(duì)Spring Cloud的分布式跟蹤工具,本文通過實(shí)例代碼介紹了SpringCloud_Sleuth分布式鏈路請(qǐng)求跟蹤,感興趣的朋友跟隨小編一起看看吧2023-02-02Spring AOP在web應(yīng)用中的使用方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Spring AOP在web應(yīng)用中的使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring AOP具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12java實(shí)現(xiàn)ssh連接服務(wù)器的方法步驟
本文主要介紹了java實(shí)現(xiàn)ssh連接服務(wù)器的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09SpringBoot+Apache tika實(shí)現(xiàn)文檔內(nèi)容解析的示例詳解
Apache tika是Apache開源的一個(gè)文檔解析工具,本文主要為大家介紹了如何在springboot中引入tika的方式解析文檔,感興趣的小伙伴可以了解一下2023-07-07探討:使用httpClient在客戶端與服務(wù)器端傳輸對(duì)象參數(shù)的詳解
本篇文章是對(duì)使用httpClient在客戶端與服務(wù)器端傳輸對(duì)象參數(shù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06解決Mybatis出現(xiàn)報(bào)錯(cuò)Error querying database.Cause: j
這篇文章主要介紹了解決Mybatis出現(xiàn)報(bào)錯(cuò)Error querying database.Cause: java.lang.IndexOutOfBoundsException: Index 9 out of,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05IDEA?database和datagrip無(wú)法下載驅(qū)動(dòng)問題解決辦法
這篇文章主要給大家介紹了關(guān)于IDEA?database和datagrip無(wú)法下載驅(qū)動(dòng)問題的解決辦法,文中通過代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用idea具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-03-03Java實(shí)現(xiàn)上傳文件圖片到指定服務(wù)器目錄
本文通過實(shí)例代碼給大家介紹了java上傳文件圖片到指定服務(wù)器目錄的相關(guān)知識(shí),代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06