JPA實體繼承實體的映射策略

注:這裏所說的實體指的是@Entity註解的類java

繼承映射使用@Inheritance來註解,它的strategy屬性的取值由枚舉InheritanceType來定義(包含SINGLE_TABLE、TABLE_PER_CLASS、JOINED,分別相應三種繼承策略)。@Inheritance註解僅僅能做用於繼承結構的超類上。假設不指定繼承策略,默認使用SINGLE_TABLE。app


JPA提供了三種繼承映射策略:
一、 一個類繼承結構一個表的策略。這是繼承映射的默認策略。即假設實體類B繼承實體類A,實體類C也繼承自實體A,那麼僅僅會映射成一個表,這個表中包含了實體類A、B、C中所有的字段。JPA使用一個叫作「discriminator列」來區分某一行數據是應該映射成哪一個實體。註解爲:@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
二、 聯合子類策略。這樣的狀況下子類的字段被映射到各自的表中,這些字段包含父類中的字段。並運行一個join操做來實例化子類。註解爲:@Inheritance(strategy = InheritanceType.JOINED)
三、 每個詳細的類一個表的策略。註解爲:@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

1、一個類繼承結構一個表的策略
這樣的策略中,一個繼承結構中的所有類都被映射到一個表中。該表中有一列被看成「discriminator列」,即便用該列來識別某行數據屬於某個指定的子類實例。
這樣的映射策略對實體和涉及類繼承結構的查詢的多態系統提供了很是好的支持。但缺點是要求與子類的指定狀態相應的列可以爲空。


實比例如如下:ide

package com.mikan;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name = "EMP")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "emp_type")
public class Employee implements Serializable {

	private static final long serialVersionUID = -7674269980281525370L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	protected Integer empId;
	
	@Column
	protected String name;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("FT")
public class FullTimeEmployee extends Employee {

	private static final long serialVersionUID = 9115429216382631425L;

	@Column
	private Double salary;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("PT")
public class PartTimeEmployee extends Employee {

	private static final long serialVersionUID = -6122347374515830424L;

	@Column(name = "hourly_wage")
	private Float hourlyWage;

	// getter/setter方法

}
當中。超類的@DiscriminatorColumn註解可以省略。默認的「discriminator列」名爲DTYPE。默認類型爲STRING。
@DiscriminatorColumn註解僅僅能使用在超類上。不能使用到詳細的子類上。discriminatorType的值由DiscriminatorType枚舉定義,包含STRING、CHAR、INTEGER。

假設指定了discriminatorType,那麼子類上@ DiscriminatorValue註解的值也應該是對應類型。
@DiscriminatorValue註解僅僅能使用在詳細的實體子類上。相同@DiscriminatorValue註解也可以省略,默認使用類名做爲值。
上面的樣例中,僅僅會生成一個表,包括了字段emp_type、empId、name、salary、hourly_wage。當保存FullTimeEmployee時,emp_type的值爲「FT」, 當保存PartTimeEmployee時。emp_type的值爲「PT」。性能



2、聯合子類策略
這樣的策略超類會被映射成一個單獨的表,每個子類也會映射成一個單獨的表。子類相應的表中僅僅包含自身屬性相應的字段,默認狀況下使用主鍵做爲超類相應的表的外鍵。url


這樣的策略對於實體間的多態關係提供了很是好的支持。spa

但缺點是實例化子類實例時需要一個或多個表的關聯操做。在深層次的繼承結構中,這會致使性能很是低。hibernate

實比例如如下:
code

package com.mikan;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name = "EMP")
@Inheritance(strategy = InheritanceType.JOINED)
public class Employee implements Serializable {

	private static final long serialVersionUID = -7674269980281525370L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	protected Integer empId;
	
	@Column
	protected String name;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "FT_EMP")
public class FullTimeEmployee extends Employee {

	private static final long serialVersionUID = 9115429216382631425L;

	@Column
	private Double salary;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "PT_EMP")
public class PartTimeEmployee extends Employee {

	private static final long serialVersionUID = -6122347374515830424L;

	@Column(name = "hourly_wage")
	private Float hourlyWage;

	// getter/setter方法

}
這會映射成三個詳細的表,各自是,Employee相應EMP表。字段包含empId、name;FullTimeEmployee相應FT_EMP表,字段包含empId、salary;PartTimeEmployee相應PT_EMP表,字段包含empId、hourly_wage。當中,表FT_EMP和PT_EMP中的empId做爲表EMP的外鍵,同是它也是主鍵。默認狀況下,使用超類的主鍵做爲子類的主鍵和外鍵。固然,可以經過@PrimaryKeyJoinColumn註解來本身指定外鍵的名稱。如FullTimeEmployee使用@PrimaryKeyJoinColumn(name = "FT_EMPID")註解。那麼該子類實體的字段爲FT_EMPID、name,FT_EMPID做爲表FT_TIME的主鍵,同一時候它也是EMP表的外鍵。
子類實體每保存一條數據,會在EMP表中插入一條記錄,如FT_EMP表插入一條數據。會先在EMP表中插入name。並生成empId。再在FT_EMP表中插入empId和salary。PT_EMP同理。
不管超類是抽象類仍是詳細類,都會生成相應的表。



3、每個詳細的類一個表的策略
這樣的映射策略每個類都會映射成一個單獨的表,類的所有屬性。包含繼承的屬性都會映射成表的列。
這樣的映射策略的缺點是:對多態關係的支持有限,當查詢涉及到類繼承結構時一般需要發起SQL UNION查詢。實比例如如下:
blog

package com.mikan;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name = "EMP")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Employee implements Serializable {

	private static final long serialVersionUID = -7674269980281525370L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.TABLE)
	protected Integer empId;
	
	@Column
	protected String name;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "FT_EMP")
public class FullTimeEmployee extends Employee {

	private static final long serialVersionUID = 9115429216382631425L;

	@Column
	private Double salary;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "PT_EMP")
public class PartTimeEmployee extends Employee {

	private static final long serialVersionUID = -6122347374515830424L;

	@Column(name = "hourly_wage")
	private Float hourlyWage;

	// getter/setter方法

}
這會映射成三個詳細的表,各自是,Employee相應EMP表,字段包含empId、name;FullTimeEmployee相應FT_EMP表,字段包含empId、salary;PartTimeEmployee相應PT_EMP表,字段包含empId、hourly_wage。當中。表FT_EMP和PT_EMP中的empId和EMP表的empId沒有不論什麼關係。子類實體每保存一條數據,EMP表中不會插入記錄。


而且主鍵的生成策略不能使用GenerationType.AUTO或GenerationType.IDENTITY,不然會出現異常:
org.hibernate.MappingException: Cannot use identity column key generation with <union-subclass> mapping for: com.mikan.PartTimeEmployee
繼承

因爲TABLE_PER_CLASS策略每個表都是單獨的,沒有並且各表的主鍵沒有不論什麼關係,因此不能使用GenerationType.AUTO或GenerationType.IDENTITY主鍵生成策略,可以使用GenerationType.TABLE。

詳細可參考:http://stackoverflow.com/questions/916169/cannot-use-identity-column-key-generation-with-union-subclass-table-per-clas

假設超類是抽象類。那麼不會生成相應的表。假設超類是詳細的類。那麼會生成相應的表。

以上實例使用JPA的hibernate實現測試經過。

相關文章
相關標籤/搜索