跳至主要内容

參考實作 Entity 型別對齊狀態

appfuse-serverapp-tenant-serverapp-tenantless-server 的新建 schema 基線已對齊 JPA Entity 欄位型別規範。本頁只保留 既有安裝升級與完整資料庫矩陣尚待執行的工作,不重複長期設計規範。

已落地

  • 時間點改用 Instant,由應用 Clock 提供現在;Spring Data auditing 也共用同一時鐘。

  • deliveryDateactivationDatedeactivationDate 分別收斂為 deliveryAtactivatedAtdeactivatedAt;移除重複的手動建立時間。

  • /api/v1/system/info 公布 server Clock 的 IANA 業務時區;參考實作已明確選定 deliveryAt 採業務時區,因此 app-office 以同一 zone 組合、反投影與篩選,MSW 亦使用 相同規則。這是該欄位經確認的例外,不是所有前端 Instant 的預設。

  • Password Entity 欄位收斂為 passwordHash;MailSetting 只持久化 AES-256-GCM ciphertext,明文只存在 request 與 mail adapter 邊界。

  • JSON converter 遇無效資料 fail fast;大型文字、JSON 與大型 byte[] 統一使用 Length.LONG32,不使用 @Lob 或資料庫原生 JSON。

  • 必填二態改用 primitive boolean;全域啟用 nationalized character data。

    後續修正(2026-07-31):上述全域啟用決策已撤回。Hibernate 7.2 的 MySQL dialect 不會因此退回 utf8mb3,但此 flag 會跨 dialect 改變所有 String 的 JDBC 與 DDL 映射。框架與參考實作現已移除該 設定,改由各資料庫的 Unicode 字元集/collation baseline 保證。

  • 框架與兩個參考實作的 generated surrogate ID 已由 Java String 收斂為 UUID,並明確映射為 portable VARCHAR(36);business / protocol key 仍保留其語意型別。

  • 框架與兩個參考實作都加入 architecture test,阻止禁用時間型別、 @Lob、原生 JSON mapping 與 magic large length 回流。

Customer.birthdayCustomer.cooperationStartDateImportantDate.date 維持 LocalDate:它們在參考領域中是純日曆事實, 不代表可推導逾期或耗時的時間點。OrderStatusHistory.metadata 的 schema 刻意允許事件型別擴充,故保留動態 JSON object。

既有安裝升級前置

參考實作目前使用 Hibernate ddl-auto,不提供單一方言的 migration 腳本。已有資料的系統不得直接靠 ddl-auto=update 套用本次語意變更; 應由採用端依自己的 migration 工具與有效業務時區完成下列搬移:

舊欄位新欄位搬移規則
account.passwordaccount.password_hash原值已是 encoded hash,原樣搬移
account.activation_dateaccount.activated_at以帳號有效規則的時間與 zone 解析成 UTC 時間點
account.deactivation_dateaccount.deactivated_at以停用生效規則的時間與 zone 解析成 UTC 時間點
orders.delivery_dateorders.delivery_at結合配送時段/截止規則與配送地 zone 解析
order_status_history.timestamporder_status_history.changed_at原時間點原樣搬移
mail_setting.smtp_use_ssl同欄位先將既有 NULL 回填為 false,再加 NOT NULL
mail_setting.smtp_use_starttls同欄位先將既有 NULL 回填為 true(沿用新列預設),再加 NOT NULL
mail_setting.smtp_password同欄位plaintext 上限 1000 字元;schema 以 Length.LONG32 容納密文;只有確實存在舊 credential 時,才用 appfuse/mail/v1 purpose 離線轉換為 v1:<key-id>: ciphertext
mail_setting.oauth2_client_secret同欄位plaintext 上限 1000 字元;schema 以 Length.LONG32 容納密文;只有確實存在舊 credential 時,才用 appfuse/mail/v1 purpose 離線轉換為 v1:<key-id>: ciphertext
generated surrogate PK 與對應 FK同欄位DB 欄位維持 VARCHAR(36);先驗證所有值都是 canonical UUID。若存在舊的非 UUID 值,建立舊值到新 UUID 的對照表,在同一 migration 中先更新所有 FK、再更新 PK,並驗證無 orphan

重複時間欄位(例如檔案 created_at 與 audit created_date)應先比對資料, 確認保留來源後再刪除。搬移流程必須先新增欄位、回填與驗證,再切換應用, 最後才移除舊欄位;時間不可用任意午夜或伺服器預設時區猜測。

應用層 persistence-encryption feature 的 root key(環境變數 APP_PERSISTENCE_ENCRYPTION_KEY)必須是 Base64 編碼的 32 bytes 隨機金鑰,並由部署 secret 管理。mail 使用固定 appfuse/mail/v1 purpose 經 HKDF 衍生專用 key,不另設 mail key。 遺失 root key 即無法取回既有憑證;輪替時先把新 key 加入 key ring、切換 active key,待舊密文 重加密並驗證完成後才撤除舊 key。明文列不被新版程式自動接受,以免把未完成 migration 靜默帶入正式環境。

reference implementation 與目前下游在導入前沒有已保存的 MailSetting credential,因此沒有 資料轉換需求,也不附帶 startup migration runner;新 schema 直接依目前 entity 基線建立。

尚待完成

  • 在 MySQL、PostgreSQL、SQL Server、Oracle、H2 執行同一套 persistence contract tests,涵蓋 Instant 微秒 round trip、JSON null/empty/ Unicode/invalid、large text/binary 邊界與 optimistic lock。

完成五資料庫矩陣後即可移除本頁。