前言:被抹除的時光
在 iDempiere 的江湖裡,有一種暗器非常難防——那是 Core 內建的 PO_BeforeUpdate 攔截器。每當 Request 事件發生時,這股暗器會無聲無息地將 EndTime 欄位抹除,讓原本完整的記錄變得殘缺不全。
更棘手的是,這招暗器發動得極快,往往在一般 Plugin 察顯之前,數據就已經被清空了。你若試圖用傳統的 BeforeUpdate 去攔截,只會發現面對的是一片空白。
核心危機:神不知鬼不覺的清除
核心挑戰在於執行順序:Core 的清除動作優先於一般 Plugin。
- 暗器已發: Core 執行清除。
- Plugin 介入:此時欄位已為空,攔截無效。
三式心法:搶先記憶,事後回補
要破解此邪功,不能硬碰硬,必須採用「三階段」的防禦架構。
心法口訣:「搶先記憶,事後回補。暗器既出,真跡不失。」
第一式:捕捉真跡 — BeforeSave 記憶
我們不能在 BeforeUpdate 等待,必須更早一步,在 EndTime 的真值,暫存在內建的 Caches2Go 煉功場中。
if ("BeforeSave@MRequest".equals(eventType)) {
Timestamp endTime = record.getTimestamp("EndTimestamp");
if (endTime != null) {
CACHE.createKey(record.getAD_Client_ID(), record.get_ID()).set(record);
}
}第二式:承受衝擊 — 忍受 Core 清除
此時,任由 Core 的 BeforeUpdate 執行其清除動作。雖然欄位在資料庫層面被抹去了,但我們最珍貴的真跡,已經安全地鎖在 Cache 裡面了。
第三式:重塑時空 — AfterUpdate 回補
最後,我們在 AfterUpdate 階段悄悄現身。從 Cache 中撈回原本的 EndTime,重新寫回紀錄中,完成時光的修復。
if ("AfterUpdate@MRequest".equals(eventType)) {
MRequest cached = CACHE.get(record.getAD_Client_ID(), record.get_ID());
if (cached != null && cached.get_EndTimestamp() != null) {
record.setTimestamp("EndTimestamp", cached.get_EndTimestamp());
record.saveUpdate();
}
}練功要點:使用 Caches2Go 煉功場
建議使用 iDempiere 內建的 Caches2Go。它有完善的 TTL(生存時間)機制,能確保記憶不會無限膨脹,也避免了傳統 Static Map 在多節點環境下失效的風險。
雷公李曰:「余觀 iDempiere 之變,其術甚詭。Core 之清除,如暗器襲人,不可阻也;然我輩若能於其發動前,先以 Cache 為盾,待其既成,再以 AfterUpdate 為劍,回補真值,則此邪功自破。此乃『攻守兼備,先奪後補』之至理也。」