OpenJPA 是最新的嘗試,它能夠?qū)ο罄^承關(guān)系的持久化透明化,企業(yè)應(yīng)用開發(fā)者僅需要處理對象模型,而不需要處理和關(guān)系型數(shù)據(jù)庫有關(guān)的內(nèi)容,極大地降低了對象繼承關(guān)系持久化的難度。下面我們來了解 OpenJPA 中持久化對象繼承關(guān)系的幾種方式。
我們從關(guān)系數(shù)據(jù)庫角度看對象繼承關(guān)系的持久化這個問題域:對象繼承通常意味著子類比父類提供更多的屬性,持久化對象繼承關(guān)系的實質(zhì)就是如何根據(jù)對象的類型動態(tài)的處理這些多出來的屬性。OpenJPA 框架支持使用三種不同的策略處理對象繼承關(guān)系:
在這種情況下,類及其子類都保存在同一張數(shù)據(jù)表中,該表提供足夠的字段保存類及其子類的所有屬性,同時提供一個特別字段保存當(dāng)前記錄對應(yīng)類的實際類名(默認(rèn)名 DTYPE,也可以在開發(fā)時指定其它名稱)。在企業(yè)應(yīng)用運行過程中,OpenJPA 框架根據(jù) Java 對象的實際類型和數(shù)據(jù)庫表進行綁定。
圖 1. Animal、Fish 和 Dog 對象模型
以上圖中提到的對象模型為例: Animal、Fish、Dog 三個類的所有對象實例都被保存在 Animal 數(shù)據(jù)表中,該表將會有 5 個屬性,其中 ID,NAME 字段對應(yīng) ANIMAL 類的兩個屬性,ID、NAME、SEX 對應(yīng) Dog 類的屬性,ID、NAME、STERRITORY 對應(yīng) Fish 類的屬性。DTYPE 是 OpenJPA 加入的字段,用于確定當(dāng)前記錄的實際類類型,在這里例子中,它的內(nèi)容是“ANIMAL”、“FISH”或者是“DOG”。
圖 2. 第一種策略的數(shù)據(jù)庫表現(xiàn)

2. 類和子類分別保存在不同的數(shù)據(jù)庫表中,互相之間沒有關(guān)聯(lián)
這種情況下,開發(fā)者不理會類之間是否存在繼承關(guān)系,為每一個類的持久化使用唯一的表,父類對象保存在父類對應(yīng)的表中,子類對象的信息保存在子類對應(yīng)的表中,這也是通常的持久化框架采用的方式。下面這個圖顯示了這種情況下對象繼承關(guān)系數(shù)據(jù)庫中的表現(xiàn)。
以上一章節(jié)中提到的對象模型為例: Animal、Fish、Dog 三個類的對象實例都被保存在各自對應(yīng)的數(shù)據(jù)表中。下面這個圖顯示了這種情況下對象繼承關(guān)系數(shù)據(jù)庫中的表現(xiàn)。
圖 3. 第二種策略的數(shù)據(jù)庫表現(xiàn)

3. 類和子類分別保存在不同的數(shù)據(jù)庫表中,子類中不保存父類中已有的屬性,僅通過主鍵進行關(guān)聯(lián)
這種情況下,父類和子類對應(yīng)不同的表,但是子類對應(yīng)的表中不再保存父類對應(yīng)表中已經(jīng)存在的字段信息,兩個表之間通過關(guān)鍵字段關(guān)聯(lián)起來,也就是數(shù)據(jù)庫技術(shù)中通常所說的外健。這種實現(xiàn)方式是最理想化的一種,既能夠處理對象之間的繼承,又滿足了關(guān)系數(shù)據(jù)庫中對于設(shè)計范式的要求。
以上一章節(jié)中提到的對象模型為例: Animal、Fish、Dog 三個類的對象實例都被在 Animal 表中有記錄;而 Fish 對象的 TERRITORY 屬性者被 FISH 表所保存,F(xiàn)ISH 表通過 ID 和 Animal 表中的數(shù)據(jù)進行關(guān)聯(lián);而 Dog 對象的 SEX 屬性者被 Dog 表所保存,Dog 表通過 ID 和 Animal 表中的數(shù)據(jù)進行關(guān)聯(lián)。下面這個圖顯示了這種情況下對象繼承關(guān)系數(shù)據(jù)庫中的表現(xiàn)。
圖 4. 第三種策略的數(shù)據(jù)庫表現(xiàn)

這三種方式的處理對于開發(fā)者而言是透明的,無論選擇哪一種,僅僅影響數(shù)據(jù)在關(guān)系數(shù)據(jù)庫中的保存方式,對于開發(fā)者而言,只需要按照面向?qū)ο蟮姆绞讲僮鲗ο蠹瓤桑琌penJPA 框架在處理持久化操作的時候,會動態(tài)地判斷當(dāng)前對象的實際類類型(后期綁定),從而確定持久化到哪個表中。在一個企業(yè)應(yīng)用的實現(xiàn)中,開發(fā)者可以根據(jù)需要選擇這三種方式的一種或者幾種來處理對象之間的繼承關(guān)系。
OpenJPA 是一個基于注釋的持久化框架,對持久化的大多數(shù)元信息都只需要為實體類提供相應(yīng)的注釋。開發(fā)者使用注釋描述實體和數(shù)據(jù)庫表之間的映射,也采用注釋描述對象繼承關(guān)系的持久化。javax.persistence.Inheritance 注釋用來指定對象繼承關(guān)系持久化的方式。它的 strategy 屬性用于指定持久化對象繼承關(guān)系在關(guān)系數(shù)據(jù)庫中的表現(xiàn)形式,可選擇項包括 SINGLE_TABLE、JOINED 和 TABLE_PER_CLASS。它們?nèi)齻€都是 javax.persistence.InheritanceType 中定義的常量。
- SINGLE_TABLE
strategy 設(shè)置為 SINGLE_TABLE 選項表示所有類及其子類保存在同一個數(shù)據(jù)庫表中,對象的類型使用表中的特殊字段 DTYPE 進行識別。
- TABLE_PER_CLASS
strategy 設(shè)置為該選項表示每個類使用一個表。
- JOINED
strategy 設(shè)置為該選項表示父類和子類分別保存在不同的數(shù)據(jù)庫表中,子類中不保存父類對應(yīng)數(shù)據(jù)庫表中已有的屬性,僅通過主鍵進行關(guān)聯(lián)。
javax.persistence.Inheritance 注釋是類級別的注釋。需要為每一個成為父類的實體類提供 javax.persistence.Inheritance 注釋并且指定 strategy 屬性。在同一個企業(yè)應(yīng)用中,開發(fā)者可以根據(jù)實際情況選擇這三種策略中的一種,或者是幾種同時使用。