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


注:這里所說的實體指的是@Entity注解的類

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


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

一、一個類繼承結構一個表的策略
這樣的策略中,一個繼承結構中的全部類都被映射到一個表中。該表中有一列被當作“discriminator列”,即使用該列來識別某行數據屬於某個指定的子類實例。
這樣的映射策略對實體和涉及類繼承結構的查詢的多態系統提供了非常好的支持。但缺點是要求與子類的指定狀態相應的列能夠為空。


實比例如以下:

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”。



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


這樣的策略對於實體間的多態關系提供了非常好的支持。

但缺點是實例化子類實例時須要一個或多個表的關聯操作。在深層次的繼承結構中,這會導致性能非常低。

實比例如以下:

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同理。
無論超類是抽象類還是詳細類,都會生成相應的表。



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

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實現測試通過。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM