AOP+MybatisPlus 優化特殊的日誌模塊

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"今天 4ye 來和小夥伴們分享下我在項目中利用 AOP + MybatisPlus 對項目進行重構,優化系統中特殊的日誌模塊的故事啦 😄 (PS:ES 寫了一半 ~ 只能先來這個了)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實這篇文章我是從吐槽部分開始寫的 哈哈哈(不知不覺就吐槽了近千字 🤣)不過咋們還是從技術部分開始吧 ~","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/09/0997499ae2397b15550e0d85d11a613b.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"功能描述","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很久之前(大概有一年了叭,痛苦面具 🙃),老大讓我把老項目中的日誌模塊移植到新項目中,這個日誌模塊的主要功能就是針對大部分表,做下面的操作","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刪除成功時,將被刪除的數據記錄到相應的 log 表","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當修改或者插入數據成功時,將這些數據記錄到相應的 log 表","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Log 表","attrs":{}},{"type":"text","text":" 就是在 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"普通表","attrs":{}},{"type":"text","text":" 的基礎上,新增幾個字段,如操作ID和操作方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"操作ID:類似請求ID 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"操作方法: 表示這個行爲是 CRUD 中 CUD 的哪一個。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如在請求001 中刪除表A 中的某條數據,則在 LOG_A 中會記錄下這個 A 數據","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"embedcomp","attrs":{"type":"table","data":{"content":"
A表對應的各個字段和它們的值操作ID操作方法
……001D
"}}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這裏,你是否也覺得這 log 表很奇怪?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"別急,最後再吐槽下 哈哈,先來說說這個技術 🐷","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"簡單思考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以發現這些這些東西和業務無關,可以直接用 AOP 來實現,老項目中也是用了 AOP。(聽君一席話,聽君一席話 哈哈哈 🤣)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先簡單思考下會遇到什麼問題 👇","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"新增數據到相應的 log 表,意味着有很多簡單的插入語句要寫?還要考慮批量操作 🐖","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"操作id 是怎麼生成的?是直接用 uuid ,還是數據庫自增id?","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"怎麼獲取刪除前的數據信息 、更新後的數據、插入後的數據 等","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏就不賣關子啦 直接來看這個","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"日誌模塊的設計","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"操作ID","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先來看這個 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"操作 ID","attrs":{}},{"type":"text","text":" 的生成,這裏就沒啥特別的 直接定義一個 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"註解","attrs":{}},{"type":"text","text":" 如 @GenerateRequestID ,加在需要被攔截的方法上,然後我們在 AOP 中攔截它即可。功能如下 👇","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6e/6e98d1f6fb66cace655652a3d2889285.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到這個 操作ID 是直接使用 Oracle 的 Sequence 去生成的,是有序的 ,能直接看出這批數據操作的先後順序。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在增強這部分功能時,我發現之前 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"舊版本","attrs":{}},{"type":"text","text":"居然沒用 AOP 去設置這個 操作ID,而是手動在需要的地方加,而且還定義了很多的 Key 來存儲生成的這個 操作ID,可以發現對這個 ThreadLocal 也很不熟悉呀! 🙃","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"僞代碼如下 😱 (壞代碼 …… )","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void a() {\n BigDecimal transactionId = sysLogMapper.generateTransactionId();\n ThreadLocalUtil.setValue(SysLogConstants.xxx_LOG_TRANSACTION_ID, transactionId);\n ThreadLocalUtil.setValue(SysLogConstants.aaa_LOG_TRANSACTION_ID, transactionId);\n\n // 業務方法\n\n ThreadLocalUtil.remove(SysLogConstants.xxx_LOG_TRANSACTION_ID);\n ThreadLocalUtil.remove(SysLogConstants.aaa_LOG_TRANSACTION_ID);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"心細的小夥伴會不會有個小疑問,爲什麼我是在 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"請求結束","attrs":{}},{"type":"text","text":" 時纔去清除這個 操作ID,而不是在 AOP 的 After 操作中去做的😄","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實這裏是有個小插曲的,一開始我是在 AOP 的 @After 操作中去刪除這個 操作ID 的,但是呢 🙄 ,有同事將我改好的日誌模塊中的部分功能添加到之前的老項目中,而且他直接將這個註解加在 service上,結果系統出現了bug 🙄,還把我拉過去討論 😒 ,這就很無語了……","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/66/66d30efea85690624c66ef0a5054f405.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過這個 bug 是不難發現的,畢竟這個註解如果加在 service 層面,會存在 service 調用 service 的情況,這樣不僅會出現第一個 service 中生成的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"操作ID","attrs":{}},{"type":"text","text":" 被第二個 service 覆蓋,而且在第二個 service 結束後,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"操作ID","attrs":{}},{"type":"text","text":" 會被清除掉,但是這個字段是不允許爲 null 的,所以就報錯了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按理說,直接加在這個 controller 層面就沒問題了,但是討論過後,在同事的建議下,我也同意對它進行小小的升級下,將這個 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"清除操作ID","attrs":{}},{"type":"text","text":" 的行爲移動到這個 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"攔截器","attrs":{}},{"type":"text","text":" 中 👇","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Component\npublic class ThreadLocalInterceptor implements HandlerInterceptor {\n @Override\n public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n ThreadLocalUtil.clear();\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"感覺這種方式也挺簡潔的,在請求結束後直接清掉這個 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ThreadLocal","attrs":{}},{"type":"text","text":" 中的內容。🐖","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"整體設計","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接着,我們來看看這個 datalog 的生成。 新版的整體思路如下。😋 (畫出來清晰多了)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c2/c2fa7b9881d8664910bcf6a95d4e4055.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"刪除操作","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"思路如上圖~","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在刪除成功時,要將被刪除的數據寫到 Log 表 …… 😑","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏有兩種情況:(一)根據主鍵刪除 ,這種最簡單,我們直接根據主鍵查出之前的數據即可。(二)根據其他條件刪除,這裏我們自定義獲取主鍵的方法以便複用上面的方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏就不得不提下這個 MybatisPlus 的好處了~ ,借用 BaseMapper 的 selectBatchIds 方法,我們可以很輕鬆的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查詢出這些數據出來。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/30/30051a1240d5d8d6ea59f49954928f97.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲在使用 MybatisPlus 時,我們會生成相應的 Mapper 方法,而這裏就很好地體現了 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Java 多態","attrs":{}},{"type":"text","text":" 的特點,我們只需要調用 (BaseMapper) SpringUtil.getBean(XXXMapper.class).selectBatchIds(result) ;即可實現。😄","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以將這個參數掛在 @MethodLog 註解上即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Mapper\npublic interface XXXMapper extends BaseMapper {\n\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而在舊版本中,由於沒有用到 MybatisPlus ,自然寫了很多的 刪除語句 😱","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而且由於對這個 Spring 的理解不夠,出現了把 實現了同一個接口的不同子類注入到一個Map中,使用時再從其中獲取的行爲,最要命的是,這個 map 的key 不是那些子類的名稱,還寫了很多 switch case …… 😶 來獲取","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4b/4b8ef94cfc346cc8e65bc9775d82cacc.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實我們直接用 ApplicationContext 的 getBean 就可以獲取到了 🐖","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"更新,插入操作","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在更新或者插入成功後,還要將這些數據寫到 Log 表 …… 😑","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"真是太魔幻了…… 再堅持下就快到吐槽環節了😂","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏我們要考慮一個問題了—— 插入數據時,ID 是插入數據前就有的,還是插入數據後纔有的?🐷","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在項目中我們使用的是這個 Oracle,藉助它的 Sequence,我們可以先獲取這個 ID (select XX_SEQ.nextval from dual),再設置到這個對象中去,然後再插入 DB 中 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而在使用 MySQL 時,我們一般都是通過數據庫的自增ID,當數據插入後,再從這個對象中獲取到這個 ID 的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這也決定了我是在 AOP 的 Before 中記錄下這些 ID ,還是在 AfterReturning 中去獲取的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實一開始我是直接在 Before 中去記錄的,後來考慮到這種情況後,才把它移到 AfterReturning 中去的,畢竟不管你先生成還是後生成,我都可以後獲取,而且第二種模式兼容第一種模式 🐷","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更新的話也有兩種情況:(一)根據主鍵更新 ,這種最簡單,我們直接根據主鍵查出之前的數據即可。(二)根據其他條件更新,這裏我們自定義獲取主鍵的方法以便複用上面的方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般情況下,我們可以自動去獲取這個 id,有些情況比較複雜的,就提供這個手動模式,自己調用 ThreadLocal 並耦合到代碼中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後使用 Spring 的工具 BeanUtils ,將數據拷貝到日誌對象中 BeanUtils.copyProperties(model, logModel); ,再通過 BaseMapper 的 insert 方法,將數據一條條插入到 Log表。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到此,就完成了這個特殊的日誌模塊的優化了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fa/fa576ec447b8955387f32a1a4d24604b.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個模塊也爲團隊節省了 N 倍的開發時間,減少了很多冗餘,無效的代碼,也提高了這個代碼的可維護性,受到同事的肯定當時 哈哈 😝 而且好像從這時候開始,老大找我做了一些通用模塊的開發,比如 模板,郵件,Excel 等,有機會再來和小夥伴們分享下~ 😄。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"吐槽","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"終於來到這裏了,說實話,我到現在都覺得這個日誌模塊很 “","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"特別","attrs":{}},{"type":"text","text":"” 🐷,因爲這個日誌模塊不像我平時瞭解過的那些,比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求的入參,出參","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"業務","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"異常","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JVM,Nginx,Tomcat 等等","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而且奇怪的點還不是寫入 DB 這個操作,而是針對大量的表做這備份的這個行爲,比如 有這個表A,然後我還要創建相應的日誌表 LOG_A,而且只要你修改或者新增數據到表A,那麼 LOG_A 會將你更改後的 A 數據記錄下來,刪除的話,會將你刪除前的A數據記錄下來,刪除成功後才記錄到這個 LOG_A 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而且這個 LOG_A 就比表A 多了幾個字段,比如這個","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"操作行爲","attrs":{}},{"type":"text","text":"( CRUD 中 的 CUD)和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"操作ID","attrs":{}},{"type":"text","text":" (即請求ID)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這…… 簡直就是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"究極備份","attrs":{}},{"type":"text","text":"了🐖 (我實在想不出哪個詞來形容了 哈哈 我只能說太慎重了!和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"慎重勇者","attrs":{}},{"type":"text","text":" 的主角有得一比)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是我新的感悟 哈哈。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還記得那時的我十分抗拒把這個東西搬到新項目中,因爲我覺得它很多餘,這不就是把數據又寫了一遍到另一個表中,而且我覺得用網上的例子就可以了啦,就記錄下數據從什麼修改成什麼就好了,要那麼多幹嘛🤨。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c4/c4f676ed1713ef7d28b778c3f22795d5.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我當時也憋不住,就向老大表達了我的疑惑,老大和我解釋說這個日誌模塊是爲了方便找 bug,找出哪些數據是有問題的,寫這篇文章的時候我還特意再去請教了一下,他說他們的老項目用到了,但是具體怎麼用也沒說 🐖","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了搞清除這個東西的作用,額 我已經問了三個項目組的同事了,終於有個同事使用到了,就是說有一次數據出了問題,看代碼看了好久都沒有覺得哪裏有問題,後來就是靠着這個請求id,去db找那批數據出來分析,才找到問題的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過我聽完還是覺得一言難盡,什麼bug 連 debug 都不能找出來,得靠分析這批數據了🙃 雖然在新項目中還是有它的身影,但是我想了好久都沒有見到它發揮作用,可能這東西還是沒用纔好吧,甚至覺得帶來負作用…… 😮","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"坑","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"印象中在重寫這個模塊時,遇到一個很有意思的問題 如下 👇","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Mapper\npublic interface XXXMapper extends BaseMapper {\n\n @Override\n @MethodLog(method = TableAction.D,\n model = XXX.class,\n logModel = LogXXX.class,\n logModelIdName = \"xxxId\",\n mapper = XXXMapper.class,\n logMapper = LogXXXMapper.class\n )\n int deleteById(Serializable id);\n \n @Override\n @MethodLog(method = TableAction.U,\n model = XXX.class,\n logModel = LogXXX.class,\n logModelIdName = \"xxxId\",\n mapper = XXXMapper.class,\n logMapper = LogXXXMapper.class\n )\n int updateById(@Param(Constants.ENTITY) XXX entity);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"畢竟我要將 MethodLog 註解掛到方法上,只能這樣了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同時,我才發現 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"接口在繼承別的接口時,也是可以添加 @Override 註解","attrs":{}},{"type":"text","text":" ,不一定是常見的那種。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還有就是一開始我用 IDEA 幫我重寫這個 updateById 方法時,但是它幫我省掉了 @Param(Constants.ENTITY) 註解,導致這個 update 操作無法生效,因爲 MybatisPlus 中用了這個 “et” 來統一這個對象的別名~ ,不加的話無法匹配到。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"最後","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文就分享到這裏啦🐖 最近有好多人生小感悟 嘿嘿 下次再分享啦。之前定的那些文章還在計劃裏~ 在抓緊更啦!!😝","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回憶起做這個日誌模塊的過程,特別是畫一遍這個流程圖的感覺真的是太酸爽了 ,也給小夥伴們提供一個思路,簡單的 CRUD 就不要自己寫啦,能用 AOP+MybatisPlus 去操作的話會簡潔很多!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"👉 ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/Java4ye/springboot-demo-4ye","title":"","type":null},"content":[{"type":"text","text":"https://github.com/Java4ye/springboot-demo-4ye","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"喜歡的話可以 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"點贊 &","attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"關注","attrs":{}},{"type":"text","text":" 並 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"星標","attrs":{}},{"type":"text","text":" 下公衆號 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Java4ye","attrs":{}},{"type":"text","text":" 支持下 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"4ye","attrs":{}},{"type":"text","text":" 呀😝,這樣就可以第一時間收到更文消息啦🐷","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"我是4ye 咱們下期應該……很快再見!!","attrs":{}},{"type":"text","text":" 😆","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章