參考實作 Entity 型別對齊狀態
appfuse-server、app-tenant-server 與 app-tenantless-server 的新建
schema 基線已對齊
JPA Entity 欄位型別規範。本頁只保留
既有安裝升級與完整資料庫矩陣尚待執行的工作,不重複長期設計規範。
已落地
-
時間點改用
Instant,由應用Clock提供現在;Spring Data auditing 也共用同一時鐘。 -
deliveryDate、activationDate、deactivationDate分別收斂為deliveryAt、activatedAt、deactivatedAt;移除重複的手動建立時間。 -
/api/v1/system/info公布 serverClock的 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,並明確映射為 portableVARCHAR(36);business / protocol key 仍保留其語意型別。 -
框架與兩個參考實作都加入 architecture test,阻止禁用時間型別、
@Lob、原生 JSON mapping 與 magic large length 回流。
Customer.birthday、Customer.cooperationStartDate 與
ImportantDate.date 維持 LocalDate:它們在參考領域中是純日曆事實,
不代表可推導逾期或耗時的時間點。OrderStatusHistory.metadata 的 schema
刻意允許事件型別擴充,故保留動態 JSON object。
既有安裝升級前置
參考實作目前使用 Hibernate ddl-auto,不提供單一方言的 migration
腳本。已有資料的系統不得直接靠 ddl-auto=update 套用本次語意變更;
應由採用端依自己的 migration 工具與有效業務時區完成下列搬移:
| 舊欄位 | 新欄位 | 搬移規則 |
|---|---|---|
account.password | account.password_hash | 原值已是 encoded hash,原樣搬移 |
account.activation_date | account.activated_at | 以帳號有效規則的時間與 zone 解析成 UTC 時間點 |
account.deactivation_date | account.deactivated_at | 以停用生效規則的時間與 zone 解析成 UTC 時間點 |
orders.delivery_date | orders.delivery_at | 結合配送時段/截止規則與配送地 zone 解析 |
order_status_history.timestamp | order_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。
完成五資料庫矩陣後即可移除本頁。