因?yàn)殛P(guān)系數(shù)據(jù)庫(kù)的表之間不存在繼承關(guān)系,Entity 提供三種基本的繼承映射策略:
每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)
每個(gè)子類(lèi)一張表(table per subclass)
每個(gè)具體類(lèi)一張表(table per concrete class)
一、每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)
這種映射方式只需為基類(lèi)創(chuàng)建一個(gè)表即可。在表中不僅提供基類(lèi)所有屬性對(duì)應(yīng)的字段,還要提供所有子類(lèi)屬性對(duì)應(yīng)的字段,此外還需要一個(gè)字段用于區(qū)分子類(lèi)的具體類(lèi)型
要使用每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy) 策略,需要把@javax.persistence.Inheritance 注釋的strategy屬性設(shè)置為InheritanceType.SINGLE_TABLE。除非你要改變子類(lèi)的映射策略,否則@Inheritance 注釋只能放在繼承層次的基類(lèi)。通過(guò)鑒別字段的值,持久化引掣可以區(qū)分出各個(gè)類(lèi),并且知道每個(gè)類(lèi)對(duì)應(yīng)那些字段。鑒別字段通過(guò)@javax.persistence.DiscriminatorColumn 注釋進(jìn)行定義,name 屬性定義鑒別字段的列名,discriminatorType 屬性定義鑒別字段的類(lèi)型(可選值有:String, Char, Integer),如果鑒別字段的類(lèi)型為String 或Char,可以用length 屬性定義其長(zhǎng)度。@DiscriminatorValue 注釋為繼承關(guān)系中的每個(gè)類(lèi)定義鑒別值,如果不指定鑒別值,默認(rèn)采用類(lèi)名
例:
@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{ //基類(lèi)
private Long id;
private Short speed;//速度
@Id
@GeneratedValue
@Column(columnDefinition="integer")//指定使用適配Integer長(zhǎng)度的數(shù)據(jù)類(lèi)型
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@SuppressWarnings("serial")
@Entity
@DiscriminatorValue("Car")
public class Car extends Vehicle{ //Vehicle的子類(lèi)
private String engine;//發(fā)動(dòng)機(jī)
@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的子類(lèi)
private String container;//集裝箱
@Column(nullable=true,length=30)
public String getContainer() {
return container;
}
public void setContainer(String container) {
this.container = container;
}
}
分析:
可 以看出,每個(gè)子類(lèi)沒(méi)有單獨(dú)的映射,在數(shù)據(jù)庫(kù)中沒(méi)有對(duì)應(yīng)的表存在。而只有一個(gè)記錄所有自身屬性和子類(lèi)所有屬性的表,在基類(lèi)為Vehicle 的時(shí)候,Discriminator 字段的值將為Vehicle,在子類(lèi)為Car 的時(shí)候,Discriminator 字段的值將為Car,子類(lèi)為Camion 的時(shí)候,Discriminator 字段的值將為Camion。那么,如果業(yè)務(wù)邏輯要求Car 對(duì)象的engine 屬性不允許為null,顯然無(wú)法在Vehicle_Hierarchy 表中為engine 字段定義not null 約束,可見(jiàn)這種映射方式無(wú)法保證關(guān)系數(shù)據(jù)模型的數(shù)據(jù)完整性。
二、每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)
這種映射方式為每個(gè)類(lèi)創(chuàng)建一個(gè)表。在每個(gè)類(lèi)對(duì)應(yīng)的表中只需包含和這個(gè)類(lèi)本身的屬性對(duì)應(yīng)的字段,子類(lèi)對(duì)應(yīng)的表參照父類(lèi)對(duì)應(yīng)的表,使用每個(gè)子類(lèi)一張表 (table per subclass)策略,需要把@javax.persistence.Inheritance 注釋的strategy 屬性設(shè)置為InheritanceType.JOINED
@SuppressWarnings("serial")
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="Vehicle")
public class Vehicle implements Serializable{ //基類(lèi)
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") //把主鍵對(duì)應(yīng)的列名更改為CarID
public class Car extends Vehicle{ //Vehicle的子類(lèi)
private String engine;//發(fā)動(dòng)機(jī)
@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") //把主鍵對(duì)應(yīng)的列名更改為CamionID
public class Camion extends Car{ //Car的子類(lèi)
private String container;
@Column(nullable=true,length=30)
public String getContainer() {
return container;
}
public void setContainer(String container) {
this.container = container;
}
}
這種映射方式支持多態(tài)關(guān)聯(lián)和多態(tài)查詢(xún),而且符合關(guān)系數(shù)據(jù)模型的常規(guī)設(shè)計(jì)規(guī)則。在這種策略中你可以對(duì)子類(lèi)的屬性對(duì)應(yīng)的字段定義not null 約束。該策略的缺點(diǎn):
它的查詢(xún)性能不如上面介紹的映射策略。在這種映射策略下,必須通過(guò)表的內(nèi)連接或左外連接來(lái)實(shí)現(xiàn)多態(tài)查詢(xún)和多態(tài)關(guān)聯(lián)。
選擇原則:子類(lèi)屬性非常多,需要對(duì)子類(lèi)某些屬性對(duì)應(yīng)的字段進(jìn)行not null 約束,且對(duì)性能要求不是很?chē)?yán)格時(shí),優(yōu)先選擇該策略
三、每個(gè)具體類(lèi)一張表(table per concrete class)
這種映射方式為每個(gè)類(lèi)創(chuàng)建一個(gè)表。在每個(gè)類(lèi)對(duì)應(yīng)的表中包含和這個(gè)類(lèi)所有屬性(包括從超類(lèi)繼承的屬性)對(duì)應(yīng)的字段,使用每個(gè)具體類(lèi)一張表(table per concrete class)策略,需要把@javax.persistence.Inheritance 注釋的strategy 屬性設(shè)置為InheritanceType.TABLE_PER_CLASS
注意:一旦使用這種策略就意味著你不能使用AUTO generator 和IDENTITY generator,即主鍵值不能采用數(shù)據(jù)庫(kù)自動(dòng)生成.
@SuppressWarnings("serial")
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@Table(name="Vehicle")
public class Vehicle implements Serializable{ //基類(lèi)
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的子類(lèi)
private String engine;//發(fā)動(dòng)機(jī)
@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的子類(lèi)
private String container;//集裝箱
@Column(nullable=true,length=30)
public String getContainer() {
return container;
}
public void setContainer(String container) {
this.container = container;
}
}
注意:在查詢(xún)時(shí),例如: from Vehicle v
查詢(xún)所有Vehicle時(shí),因?yàn)樗亲罾^承樹(shù)中的根,查詢(xún)結(jié)果會(huì)得到所有繼承于Vehicle類(lèi)的記錄
(構(gòu)造的SQL Where部分:where Discriminator in ('Car', 'Camion'))
delete from Vehicle v
執(zhí)行該操作會(huì)刪除自身對(duì)應(yīng)記錄,還會(huì)刪除所有繼承Vehicle的記錄,因?yàn)樗亲罾^承樹(shù)中的根,就相當(dāng)于清除整個(gè)表的數(shù)據(jù)
該策略的優(yōu)點(diǎn):
在這種策略中你可以對(duì)子類(lèi)的屬性對(duì)應(yīng)的字段定義not null 約束。
該策略的缺點(diǎn):
不符合關(guān)系數(shù)據(jù)模型的常規(guī)設(shè)計(jì)規(guī)則,每個(gè)表中都存在屬于基類(lèi)的多余的字段。同時(shí),為了支持策略的映射,持久化管理者需要決定使用什么方法,一種方法是在 entity 載入或多態(tài)關(guān)聯(lián)時(shí),容器使用多次查詢(xún)?nèi)?shí)現(xiàn),這種方法需要對(duì)數(shù)據(jù)庫(kù)做幾次來(lái)往查詢(xún),非常影響執(zhí)行效率。另一種方法是容器通過(guò)使用SQLUNIOU 查詢(xún)來(lái)實(shí)現(xiàn)這種策略。
選擇原則:
除非你的現(xiàn)實(shí)情況必須使用這種策略,一般情況下不要選擇。
每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)
每個(gè)子類(lèi)一張表(table per subclass)
每個(gè)具體類(lèi)一張表(table per concrete class)
一、每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)
這種映射方式只需為基類(lèi)創(chuàng)建一個(gè)表即可。在表中不僅提供基類(lèi)所有屬性對(duì)應(yīng)的字段,還要提供所有子類(lèi)屬性對(duì)應(yīng)的字段,此外還需要一個(gè)字段用于區(qū)分子類(lèi)的具體類(lèi)型
要使用每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy) 策略,需要把@javax.persistence.Inheritance 注釋的strategy屬性設(shè)置為InheritanceType.SINGLE_TABLE。除非你要改變子類(lèi)的映射策略,否則@Inheritance 注釋只能放在繼承層次的基類(lèi)。通過(guò)鑒別字段的值,持久化引掣可以區(qū)分出各個(gè)類(lèi),并且知道每個(gè)類(lèi)對(duì)應(yīng)那些字段。鑒別字段通過(guò)@javax.persistence.DiscriminatorColumn 注釋進(jìn)行定義,name 屬性定義鑒別字段的列名,discriminatorType 屬性定義鑒別字段的類(lèi)型(可選值有:String, Char, Integer),如果鑒別字段的類(lèi)型為String 或Char,可以用length 屬性定義其長(zhǎng)度。@DiscriminatorValue 注釋為繼承關(guān)系中的每個(gè)類(lèi)定義鑒別值,如果不指定鑒別值,默認(rèn)采用類(lèi)名
例:
@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{ //基類(lèi)
private Long id;
private Short speed;//速度
@Id
@GeneratedValue
@Column(columnDefinition="integer")//指定使用適配Integer長(zhǎng)度的數(shù)據(jù)類(lèi)型
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@SuppressWarnings("serial")
@Entity
@DiscriminatorValue("Car")
public class Car extends Vehicle{ //Vehicle的子類(lèi)
private String engine;//發(fā)動(dòng)機(jī)
@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的子類(lèi)
private String container;//集裝箱
@Column(nullable=true,length=30)
public String getContainer() {
return container;
}
public void setContainer(String container) {
this.container = container;
}
}
分析:
可 以看出,每個(gè)子類(lèi)沒(méi)有單獨(dú)的映射,在數(shù)據(jù)庫(kù)中沒(méi)有對(duì)應(yīng)的表存在。而只有一個(gè)記錄所有自身屬性和子類(lèi)所有屬性的表,在基類(lèi)為Vehicle 的時(shí)候,Discriminator 字段的值將為Vehicle,在子類(lèi)為Car 的時(shí)候,Discriminator 字段的值將為Car,子類(lèi)為Camion 的時(shí)候,Discriminator 字段的值將為Camion。那么,如果業(yè)務(wù)邏輯要求Car 對(duì)象的engine 屬性不允許為null,顯然無(wú)法在Vehicle_Hierarchy 表中為engine 字段定義not null 約束,可見(jiàn)這種映射方式無(wú)法保證關(guān)系數(shù)據(jù)模型的數(shù)據(jù)完整性。
二、每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)
這種映射方式為每個(gè)類(lèi)創(chuàng)建一個(gè)表。在每個(gè)類(lèi)對(duì)應(yīng)的表中只需包含和這個(gè)類(lèi)本身的屬性對(duì)應(yīng)的字段,子類(lèi)對(duì)應(yīng)的表參照父類(lèi)對(duì)應(yīng)的表,使用每個(gè)子類(lèi)一張表 (table per subclass)策略,需要把@javax.persistence.Inheritance 注釋的strategy 屬性設(shè)置為InheritanceType.JOINED
@SuppressWarnings("serial")
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="Vehicle")
public class Vehicle implements Serializable{ //基類(lèi)
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") //把主鍵對(duì)應(yīng)的列名更改為CarID
public class Car extends Vehicle{ //Vehicle的子類(lèi)
private String engine;//發(fā)動(dòng)機(jī)
@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") //把主鍵對(duì)應(yīng)的列名更改為CamionID
public class Camion extends Car{ //Car的子類(lèi)
private String container;
@Column(nullable=true,length=30)
public String getContainer() {
return container;
}
public void setContainer(String container) {
this.container = container;
}
}
這種映射方式支持多態(tài)關(guān)聯(lián)和多態(tài)查詢(xún),而且符合關(guān)系數(shù)據(jù)模型的常規(guī)設(shè)計(jì)規(guī)則。在這種策略中你可以對(duì)子類(lèi)的屬性對(duì)應(yīng)的字段定義not null 約束。該策略的缺點(diǎn):
它的查詢(xún)性能不如上面介紹的映射策略。在這種映射策略下,必須通過(guò)表的內(nèi)連接或左外連接來(lái)實(shí)現(xiàn)多態(tài)查詢(xún)和多態(tài)關(guān)聯(lián)。
選擇原則:子類(lèi)屬性非常多,需要對(duì)子類(lèi)某些屬性對(duì)應(yīng)的字段進(jìn)行not null 約束,且對(duì)性能要求不是很?chē)?yán)格時(shí),優(yōu)先選擇該策略
三、每個(gè)具體類(lèi)一張表(table per concrete class)
這種映射方式為每個(gè)類(lèi)創(chuàng)建一個(gè)表。在每個(gè)類(lèi)對(duì)應(yīng)的表中包含和這個(gè)類(lèi)所有屬性(包括從超類(lèi)繼承的屬性)對(duì)應(yīng)的字段,使用每個(gè)具體類(lèi)一張表(table per concrete class)策略,需要把@javax.persistence.Inheritance 注釋的strategy 屬性設(shè)置為InheritanceType.TABLE_PER_CLASS
注意:一旦使用這種策略就意味著你不能使用AUTO generator 和IDENTITY generator,即主鍵值不能采用數(shù)據(jù)庫(kù)自動(dòng)生成.
@SuppressWarnings("serial")
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@Table(name="Vehicle")
public class Vehicle implements Serializable{ //基類(lèi)
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的子類(lèi)
private String engine;//發(fā)動(dòng)機(jī)
@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的子類(lèi)
private String container;//集裝箱
@Column(nullable=true,length=30)
public String getContainer() {
return container;
}
public void setContainer(String container) {
this.container = container;
}
}
注意:在查詢(xún)時(shí),例如: from Vehicle v
查詢(xún)所有Vehicle時(shí),因?yàn)樗亲罾^承樹(shù)中的根,查詢(xún)結(jié)果會(huì)得到所有繼承于Vehicle類(lèi)的記錄
(構(gòu)造的SQL Where部分:where Discriminator in ('Car', 'Camion'))
delete from Vehicle v
執(zhí)行該操作會(huì)刪除自身對(duì)應(yīng)記錄,還會(huì)刪除所有繼承Vehicle的記錄,因?yàn)樗亲罾^承樹(shù)中的根,就相當(dāng)于清除整個(gè)表的數(shù)據(jù)
該策略的優(yōu)點(diǎn):
在這種策略中你可以對(duì)子類(lèi)的屬性對(duì)應(yīng)的字段定義not null 約束。
該策略的缺點(diǎn):
不符合關(guān)系數(shù)據(jù)模型的常規(guī)設(shè)計(jì)規(guī)則,每個(gè)表中都存在屬于基類(lèi)的多余的字段。同時(shí),為了支持策略的映射,持久化管理者需要決定使用什么方法,一種方法是在 entity 載入或多態(tài)關(guān)聯(lián)時(shí),容器使用多次查詢(xún)?nèi)?shí)現(xiàn),這種方法需要對(duì)數(shù)據(jù)庫(kù)做幾次來(lái)往查詢(xún),非常影響執(zhí)行效率。另一種方法是容器通過(guò)使用SQLUNIOU 查詢(xún)來(lái)實(shí)現(xiàn)這種策略。
選擇原則:
除非你的現(xiàn)實(shí)情況必須使用這種策略,一般情況下不要選擇。