ADR-027: JSON 欄位處理策略
ADR 編號: 027 狀態: 已接受 (Accepted) 決策日期: 2026-07-29 決策者: Development Team
Context
本 ADR 取代 ADR-002;原 ADR 保留當時的方案比較與決策歷史。
部分 Entity 需要保存小型、原子、無 SQL 內部查詢需求的結構,例如地址 快照、標籤或少量 metadata。參考實作必須同時支援 MySQL、PostgreSQL、 SQL Server、Oracle 與 H2。
資料庫原生 JSON 的型別、查詢語法、索引與 Hibernate dialect 行為不同;
@Lob 也會在不同資料庫映射成不等價的 CLOB / large-object 機制。直接在
collection 上標示 JDBC LONGVARCHAR 並不會自動提供可靠的 JSON
serialization。
Decision
JSON 欄位採:
@Convert(converter = AddressListConverter.class)
@Column(length = Length.LONG32)
private List<Address> addresses = new ArrayList<>();
也就是:
- Entity 保留具體 value object / collection 型別。
- 使用
AttributeConverter<T, String>負責 JSON serialization。 - 使用
@Column(length = Length.LONG32)取得 dialect-aware 的大型文字 mapping。 - Converter 明確定義 null / empty 語意,並對無效 JSON 或 serialization failure fail fast。
不得使用:
@Lob@JdbcTypeCode(SqlTypes.JSON)- collection 上的
@JdbcTypeCode(SqlTypes.LONGVARCHAR) - DB native JSON 作為參考實作基線
- 解析失敗時回傳空集合、空物件或
null
適用邊界
JSON 只適用於同時符合以下條件的資料:
- 與 owner 一起原子更新。
- 大小與元素數有明確上限。
- SQL 不需查詢、排序、join、unique 或部分更新內部欄位。
- 內部值沒有獨立 identity / lifecycle。
不符合時改用一般欄位、@Embeddable、@ElementCollection 或 child Entity。
JSON value 優先使用具名 immutable type。只有 schema 本來就動態時才使用
Map<String, Object>。Schema 演進需要相容性測試;不能靠 silent fallback
吞掉舊資料錯誤。
Consequences
Positive
- 五種目標資料庫使用同一個文字儲存契約。
- Java 型別與 serialization 邊界明確。
- 不依賴資料庫 large-object API 或 native JSON dialect。
- 資料毀損會立即現形。
Trade-offs
- DB 無法用 portable SQL 查詢 JSON 內部欄位。
- 每種結構需要 converter 與 round-trip tests。
- 大型或高更新頻率集合仍須正規化。
Verification
每個 converter 至少驗證:
- null、empty 與一般值 round trip
- Unicode 與
java.timevalue - unknown / missing field 的相容策略
- malformed JSON 與 serialization failure 會拋錯
- MySQL、PostgreSQL、SQL Server、Oracle、H2 的 DDL 與 round trip
完整 operational rules 見 JPA Entity 欄位型別規範。
Change Log
| 日期 | 變更 |
|---|---|
| 2025-12-22 | 初版採文字型 JSON 儲存 |
| 2025-12-29 | 改採 @Lob + AttributeConverter |
| 2026-07-29 | 移除 @Lob;收斂為 typed converter + Length.LONG32,並要求 fail fast |