科技新知

筆者身邊的朋友,首次接觸 ORM 的關聯型態時都會覺得很難,筆者自己也是。但在好好地理順它的設計時,就會覺得其實很簡單。

因為篇輻很長,我們先以Code First的角度,先體驗一下ORM程式讀取的便捷性,以及解決一個常見的序列化問題。

雙向存取

例如一個Parent,有好幾個Child

@Entity
public class Parent {
    // ... Parent Primay Key
    @OneToMany(mappedBy="parent")
    List children = new ArrayList<>();
    // TODO add remove
}

@Entity
public class Child {
    // ... Child Primay Key
    @ManyToOne
    Parent parent;
}

上述的寫法很簡潔,ORM會為你自動加入join column,處理關聯的載入。在讀取Parent時,它的所有Children就可以直接在Java層面讀取,在讀取Child時,它的Parent也隨時取得。也就是,開發人員只要經SQL準備其中一方的資料,另一方並不需要手動準備,它就可以自動按需載入。

RESTFul API 坑-雙向存取

Spring Data在Java層面的雙向存取,已經做到很方便。但經常坑到我們的是Spring Data與RESTFul API的混合應用。當我們嘗試經API回傳我們的Parent Json時,API會很聰明地把關聯的Children也變成Json回傳。但他也會把child中的parent不斷重複變成json,變成無限輪迴。

坊間有兩種不同的解決方案,可以防止無限輪迴。

  • 讓Json可以認得已經序列化的元素。@JsonIdentityInfo
  • 讓Json只可以單向序列化(serialization)。@JsonManagedReference, @JsonBackReference, @JsonIgnore

筆者兩個方向都試過,但首個方法並不通用,至少它不能算是一般常見的無腦Json結構。它需要伺服器、客戶端都懂這如何經IdentityInfo認得重複出現的元素。

而單向序列化,是筆者現時的通用解。在設計RESTFul READ API時,筆者就會決定到底是Parent自動回傳Child,還是Child自動回傳Parent。決策的考慮因素,主要在於是否可以簡化Client的API調用次數。通常從Parent出發,自動回傳Child,可以節省API調用。但如果是選項性的結果(List of Value),就倒過來。有時候,遇著API需要雙向設計,就只好自己設計DTO資料傳輸對象 (Data transfer object, DTO)。

例如Parent API,就原封不動回傳原本的元素

@Entity
public class Parent{
    // ... Parent Primay Key
    @OneToMany(mappedBy="parent")
    List children = new ArrayList<>();
}

@Entity
public class Child {
    // ... Child Primay Key
    @ManyToOne
    @JsonIgnore
    Parent parent;
}

Child API,就反過來引用。

public class ParentDTO {
    // ... Parent Other fields except children
}

public class ChildDTO {
    ParentDTO parent;
    // ... Child Other fields
}

這種DTO,看起來很麻煩。但其實Spring有提供一個簡便的複制DTO功能,它可以把自動複制兩個class中有同一名稱、同一型別的欄位到另一個class上,不需要逐個欄位明文寫出來。

BeanUtils.copy(child, childDTO);
BeanUtils.copy(parent, parentDTO);
childDTO.setParent(parentDTO)
// 因為child、childDTO中的parent欄位型別不同,BeanUtils.copy會自動忽略,其他欄位就會自動複制。

註: 其實古早的網頁系統設計,DTO的概念一直存取。只是現在RESTFul API的流行,很多框架已經提向便捷的Json轉換。若然平時只需Json單向存取,筆者還是省略DTO的建立。

馬交野


破.地獄
GI-DLE WORLD TOUR iDOL IN CINEMAS
DX - IU CONCERT : THE WINNING
IMAX with Laser 封神第二部:戰火西岐
隔壁的房間
工作細胞
哈哈哈新年喜戲
射雕英雄傳:俠之大者
祥賭必贏
麻雀女王追男仔
臨時決鬥
封神第二部:戰火西岐
完美伴侶
IU CONCERT : THE WINNING 2025 字幕版
《七宗罪》30周年 4K IMAX 版本 限定重映
【我推的孩子】-The Final Act-
【我推的孩子】The Final Act
柏靈頓:秘魯大冒險
柏靈頓:秘魯大冒險英語版
GIDLE WORLD TOUR IDOL IN CINEMAS
IVE The 1st World Tour in Cinema
劇場版「進擊的巨人」完結篇
劇場版 「進擊的巨人」完結篇THE LAST ATTACK
劇場版 「進擊的巨人」完結篇 THE LAST ATTACK
假爸爸
劇場版 「進擊的巨人」完結篇
MX4D 超音鼠大電影3
破。鏡。慾
誤判
獅子王:木法沙
超音鼠大電影3
魔海奇緣 2
破.地獄
GI-DLE WORLD TOUR iDOL IN CINEMAS