EJB/JPA 繼承詳解(轉)

因爲關係數據庫的表之間不存在繼承關係,Entity 提供三種基本的繼承映射策略:
每個類分層結構一張表(table per class hierarchy)
每個子類一張表(table per subclass)
每個具體類一張表(table per concrete class)

一、每個類分層結構一張表(table per class hierarchy)
       這種映射方式只需爲基類創建一個表即可。在表中不僅提供基類所有屬性對應的字段,還要提供所有子類屬性對應的字段,此外還需要一個字段用於區分子類的具體 類型
       要使用每個類分層結構一張表(table per class hierarchy) 策略,需要把@javax.persistence.Inheritance 註釋的strategy 屬性設置爲InheritanceType.SINGLE_TABLE 。 除非你要改變子類的映射策略,否則@Inheritance 註釋只能放在繼承層次的基類 。通過鑑別字段的值,持久化引掣可以區分出各個類,並且知道每個類對應那些字段。鑑別字段 通過@javax.persistence.DiscriminatorColumn 註釋進行定義,name 屬性定義鑑別字段 的列名 discriminatorType 屬性定義鑑別字段的類型 (可選值有:String, Char, Integer),如果鑑別字段的類型爲String 或Char,可以用length 屬性定義其長度。@DiscriminatorValue 註釋爲繼承關係中的每個類定義鑑別值 ,如果不指定鑑別值,默認採用類名
例:
    @SuppressWarnings("serial")
    @Entity
    @Table(name="Vehicle_Hierarchy")
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name="Discriminator",
                                         discriminatorType = DiscriminatorType.STRING,
                                         length=30)
    @DiscriminatorValue("Vehicle")
    public class Vehicle implements Serializable{       //基類
    private Long id;
    private Short speed;//速度
    @Id
    @GeneratedValue
    @Column(columnDefinition="integer")//指定使用適配Integer長度的數據類型
    public Long getId() {
    return id;
    }
    public void setId(Long id) {
    this.id = id;
    }

    @SuppressWarnings("serial")
    @Entity
    @DiscriminatorValue("Car")
    public class Car extends Vehicle{        //Vehicle的子類
    private String engine;//發動機
    @Column(nullable=true,length=30)
    public String getEngine() {
    return engine;
    }
    public void setEngine(String engine) {
    this.engine = engine;
        }
    }


     @SuppressWarnings("serial")
     @Entity
     @DiscriminatorValue("Camion")
     public class Camion extends Car{         //Car的子類
     private String container;//集裝箱
     @Column(nullable=true,length=30)
     public String getContainer() {
     return container;
     }
     public void setContainer(String container) {
     this.container = container;
          }
     }
分析:
       可 以看出,每個子類沒有單獨的映射,在數據庫中沒有對應的表存在。而只有一個記錄所有自身屬性和子類所有屬性的表,在基類爲Vehicle 的時候,Discriminator 字段的值將爲Vehicle,在子類爲Car 的時候,Discriminator 字段的值將爲Car,子類爲Camion 的時候,Discriminator 字段的值將爲Camion。那麼,如果業務邏輯要求Car 對象的engine 屬性不允許爲null,顯然無法在Vehicle_Hierarchy 表中爲engine 字段定義not null 約束,可見這種映射方式無法保證關係數據模型的數據完整性。

二、每個類分層結構一張表(table per class hierarchy)
       這種映射方式爲每個類創建一個表。在每個類對應的表中只需包含和這個類本身的屬性對應的字段,子類對應的表參照父類對應的表,使用每個子類一張表 (table per subclass)策略,需要把@javax.persistence.Inheritance 註釋的strategy 屬性設置爲InheritanceType.JOINED

     @SuppressWarnings("serial")
     @Entity
     @Inheritance(strategy=InheritanceType.JOINED)
     @Table(name="Vehicle")
     public class Vehicle implements Serializable{      //基類
     private Long id;
     private Short speed;//速度
     @Id
     @GeneratedValue
     @Column(columnDefinition="integer")
     public Long getId() {
     return id;
     }
     public void setId(Long id) {
     this.id = id;
     }
     public Short getSpeed() {
     return speed;
     }
     public void setSpeed(Short speed) {
     this.speed = speed;
     }
     }

     @SuppressWarnings("serial")
     @Entity
     @Table(name="Car")
     @PrimaryKeyJoinColumn(name="CarID")     //把主鍵對應的列名更改爲CarID
     public class Car extends Vehicle{                 //Vehicle的子類
     private String engine;//發動機
     @Column(nullable=true,length=30)
     public String getEngine() {
     return engine;
     }
     public void setEngine(String engine) {
     this.engine = engine;
     }
     }

     @SuppressWarnings("serial")
     @Entity
     @Table(name="Camion")
     @PrimaryKeyJoinColumn(name="CamionID")     //把主鍵對應的列名更改爲CamionID
     public class Camion extends Car{                    //Car的子類
     private String container;
     @Column(nullable=true,length=30)
     public String getContainer() {
     return container;
     }
     public void setContainer(String container) {
     this.container = container;
     }
     }
        這種映射方式支持多態關聯和多態查詢,而且符合關係數據模型的常規設計規則。在這種策略中你可以對子類的屬性對應的字段定義not null 約束。該策略的缺點:
        它的查詢性能不如上面介紹的映射策略。在這種映射策略下,必須通過表的內連接或左外連接來實現多態查詢和多態關聯。
選擇原則:子類屬性非常多,需要對子類某些屬性對應的字段進行not null 約束,且對性能要求不是很嚴格時,優先選擇該策略


三、每個具體類一張表(table per concrete class)
       這種映射方式爲每個類創建一個表。在每個類對應的表中包含和這個類所有屬性(包括從超類繼承的屬性)對應的字段,使用每個具體類一張表(table per concrete class)策略,需要把@javax.persistence.Inheritance 註釋的strategy 屬性設置爲InheritanceType.TABLE_PER_CLASS

        注意:一旦使用這種策略就意味着你不能使用AUTO generator 和IDENTITY generator,即主鍵值不能採用數據庫自動生成.

     @SuppressWarnings("serial")
     @Entity
     @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
     @Table(name="Vehicle")
     public class Vehicle implements Serializable{            //基類
     private Long id;
     private Short speed;//速度
     @Id
     @Column(columnDefinition="integer")
     public Long getId() {
     return id;
     }
     public void setId(Long id) {
     this.id = id;
     }
     public Short getSpeed() {
     return speed;
     }
     public void setSpeed(Short speed) {
     this.speed = speed;
     }
     }

     @SuppressWarnings("serial")
     @Entity
     @Table(name="Car")
    public class Car extends Vehicle{               //Vehicle的子類
     private String engine;//發動機
     @Column(nullable=true,length=30)
     public String getEngine() {
     return engine;
     }
     public void setEngine(String engine) {
     this.engine = engine;
     }
     }

     @SuppressWarnings("serial")
     @Entity
     @Table(name="Camion")
     public class Camion extends Car{               //Car的子類
     private String container;//集裝箱
     @Column(nullable=true,length=30)
     public String getContainer() {
     return container;
     }
     public void setContainer(String container) {
     this.container = container;
     }
     }

注意:在查詢時,例如: from Vehicle v
         查詢所有Vehicle時,因爲他是最繼承樹中的根,查詢結果會得到所有繼承於Vehicle類的記錄
(構造的SQL Where部分:where Discriminator in ('Car', 'Camion') )
         delete from Vehicle v
         執行該操作會刪除自身對應記錄,還會刪除所有繼承Vehicle的記錄,因爲他是最繼承樹中的根,就相當於清除整個表的數據

該策略的優點:
                     在這種策略中你可以對子類的屬性對應的字段定義not null 約束。
該策略的缺點:
                     不符合關係數據模型的常規設計規則,每個表中都存在屬於基類的多餘的字段。同時,爲了支持策略的映射,持久化管理者需要決定使用什麼方法,一種方法是在 entity 載入或多態關聯時,容器使用多次查詢去實現,這種方法需要對數據庫做幾次來往查詢,非常影響執行效率。另一種方法是容器通過使用SQLUNIOU 查詢來實現這種策略。
選擇原則:
                     除非你的現實情況必須使用這種策略,一般情況下不要選擇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章