iDempiere

iDempiere 守時秘術:破解 EndTime 被抹除的邪功

2026-04-19 · 2 分鐘 ·

前言:被抹除的時光

在 iDempiere 的江湖裡,有一種暗器非常難防——那是 Core 內建的 PO_BeforeUpdate 攔截器。每當 Request 事件發生時,這股暗器會無聲無息地將 EndTime 欄位抹除,讓原本完整的記錄變得殘缺不全。

更棘手的是,這招暗器發動得極快,往往在一般 Plugin 察顯之前,數據就已經被清空了。你若試圖用傳統的 BeforeUpdate 去攔截,只會發現面對的是一片空白。

核心危機:神不知鬼不覺的清除

核心挑戰在於執行順序:Core 的清除動作優先於一般 Plugin。

  • 暗器已發: Core 執行清除。
  • Plugin 介入:此時欄位已為空,攔截無效。

三式心法:搶先記憶,事後回補

要破解此邪功,不能硬碰硬,必須採用「三階段」的防禦架構。

心法口訣:「搶先記憶,事後回補。暗器既出,真跡不失。」

第一式:捕捉真跡 — BeforeSave 記憶

我們不能在 BeforeUpdate 等待,必須更早一步,在 BeforeSave 階段就介入。當資料進入存檔流程的第一時間,我們就將 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 為劍,回補真值,則此邪功自破。此乃『攻守兼備,先奪後補』之至理也。」

作者