这篇blog准备写怎样在Hibernate中反应内存对象之间的继承关系.我们知道,内存对象之间是可以有继承关系的,但是在数据库中,我们继承神马的都是浮云,不存在的.那么怎么办呢?Hibernate提供了继承关系映射!(其实就是帮你把原来的一个类以一种特定的方式存储在数据库中.)
我们以官方的例子为例:
分为四种方式:
1. MappedSuperclass
被继承的表在数据库中不映射.
@MappedSuperclass
public static class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity(name = "DebitAccount")
public static class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity(name = "CreditAccount")
public static class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}
CREATE TABLE DebitAccount (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
overdraftFee NUMERIC(19, 2) ,
PRIMARY KEY ( id )
)
CREATE TABLE CreditAccount (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
creditLimit NUMERIC(19, 2) ,
PRIMARY KEY ( id )
)
没有创建Acount表,但是Account的属性在每个子类的数据库表中都存在.
2. Single table
单表映射,所有的类映射到数据库中的一个表
Annotation版本:
@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public static class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity(name = "DebitAccount")
public static class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity(name = "CreditAccount")
public static class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}
CREATE TABLE Account (
DTYPE VARCHAR(31) NOT NULL ,
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
overdraftFee NUMERIC(19, 2) ,
creditLimit NUMERIC(19, 2) ,
PRIMARY KEY ( id )
)
Account Table:
DTYPE | id | balance | owner | interestRate | overdraftFee | creditLimit |
---|---|---|---|---|---|---|
描述列(表明这是什么类的实例对象) | 主键 | Account属性 | Account属性 | Account属性 | DebitAccount属性(CreditAccount中为空) | CreditAccount属性(在DebitAccount中为空) |
Single table版本,需要在Account表格中添加所有子类可能的属性以及对于类的描述性列(,因为子父类都在一个表中,我们需要知道一个元组是什么类,所以必须有一个描述列更多关于描述列,这里DTYPE即是它的描述性列,只能为String,Char,INTEGER),因此会有大量的数据冗余.
注解版本使用另外三个类:
Plane(父类),Airbus(子类),Boeing(子类)
public class Plane {
private Long id;
private String type;
private String manufacturer;
public Plane() {}
public Long getId() { return id; }
private void setId(Long id) { this.id = id; }
public String getType() { return type; }
public void setType(String type) {
this.type = type;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
}
public class Airbus extends Plane{
private String capacity;
public Airbus() {}
public String getCapacity() { return capacity; }
private void setCapacity(String capacity) {
this.capacity = capacity;
}
}
public class Boeing extends Plane{
private String comfort;
public Boeing() {}
public String getComfort() { return comfort; }
private void setComfort(String comfort) {
this.comfort = comfort;
}
}
配置文件:
<hibernate-mapping package="Sample.Entity"
discriminator-value="0“ >
<class name="Plane" table="planes">
<id name="id" column="ID">
<generator class="native"/>
</id>
<discriminator column="DISC" type="string"/>
<property name="type"/>
<property name="manufacturer"/>
<subclass name="Airbus" discriminator-value="1">
<property name="capacity" />
</subclass>
<subclass name="Boeing" discriminator-value="2">
<property name="comfort" />
</subclass>
</class>
</hibernate-mapping>
优点:效率最高,不需要多表联合查询.
缺点:会产生太多的数据冗余,大量的null字段,导致完整性约束不能在数据库层面上使用,只能依靠数据操作层维持.
3. Joined table
@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.JOINED)
public static class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity(name = "DebitAccount")
public static class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity(name = "CreditAccount")
public static class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}
CREATE TABLE Account (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
PRIMARY KEY ( id )
)
CREATE TABLE CreditAccount (
creditLimit NUMERIC(19, 2) ,
id BIGINT NOT NULL ,
PRIMARY KEY ( id )
)
CREATE TABLE DebitAccount (
overdraftFee NUMERIC(19, 2) ,
id BIGINT NOT NULL ,
PRIMARY KEY ( id )
)
ALTER TABLE CreditAccount
ADD CONSTRAINT FKihw8h3j1k0w31cnyu7jcl7n7n
FOREIGN KEY (id) REFERENCES Account
ALTER TABLE DebitAccount
ADD CONSTRAINT FKia914478noepymc468kiaivqm
FOREIGN KEY (id) REFERENCES Account
创建了三个表格,子表中只存在子类的自身属性加上父类的外键约束.
因为在查询的时候,语句大约是:
select ...
from Account
... Join DebitAccount
... Join CreditAccount.
这种策略使用join 操作父子类表.
配置文件版本:
public class Cat {
private Long uid;
private Date birthday;
private String color;
private String sex;
private int weight;
public Cat() {
}
public Long getUid() {
return uid;
}
private void setUid(Long uid) {
this.uid = uid;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
public class DomesticCat extends Cat {
private String name;
public DomesticCat() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<hibernate-mapping package="Sample.Entity">
<class name="Cat" table="CATS">
<id name="uid" column="UID">
<generator class="native"/>
</id>
<property name="birthday" type="date"/>
<property name="color"/>
<property name="sex"/>
<property name="weight"/>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>
</hibernate-mapping>
映射到多张表中,但是子表并没有父表的属性,只有一个父表的外键.
优点:减少了数据冗余
缺点:数据量太大是,join操作会影响性能
4. Table per class
每个表一个映射表,注意:此时ID生成策略不能使用generate,需要手动保证子父表之间ID的唯一性,可以考虑使用generateTable方法.
@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public static class Account {
@Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public BigDecimal getInterestRate() {
return interestRate;
}
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
}
@Entity(name = "DebitAccount")
public static class DebitAccount extends Account {
private BigDecimal overdraftFee;
public BigDecimal getOverdraftFee() {
return overdraftFee;
}
public void setOverdraftFee(BigDecimal overdraftFee) {
this.overdraftFee = overdraftFee;
}
}
@Entity(name = "CreditAccount")
public static class CreditAccount extends Account {
private BigDecimal creditLimit;
public BigDecimal getCreditLimit() {
return creditLimit;
}
public void setCreditLimit(BigDecimal creditLimit) {
this.creditLimit = creditLimit;
}
}
CREATE TABLE Account (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
PRIMARY KEY ( id )
)
CREATE TABLE CreditAccount (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
creditLimit NUMERIC(19, 2) ,
PRIMARY KEY ( id )
)
CREATE TABLE DebitAccount (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
overdraftFee NUMERIC(19, 2) ,
PRIMARY KEY ( id )
)
注意生成了三张表,对应三个类,并且每个类的主键都没有依赖,为了保证每个对象都是有唯一的id,(内存中三个对象id都需要不同)我们需要手动保证三个table的id列都是唯一的,比如说,Account中有一个元组中id为1,那么DebitAccount中就不能有元组id为1.同理,DebitAccount中有id为1,其他两个表也是一样.
查询的时候,三个表之间使用了union查询语句.
配置版本:
public class Fruit {
private Long id;
private String shape;
private String flavor;
private String color;
public Fruit() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getShape() {
return shape;
}
public void setShape(String shape) {
this.shape = shape;
}
public String getFlavor() {
return flavor;
}
public void setFlavor(String flavor) {
this.flavor = flavor;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class Banana extends Fruit{
private int length;
public Banana() {
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
public class Apple extends Fruit {
private int weight;
public Apple() {
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
<hibernate-mapping package="entities">
<class name="entities.Fruit" table="`fruit`">
<id name="id" column="ID">
<!--table per class 策略不能使用 generate 策略,否则无法生成sessionFactory.可以考虑使用generateTable-->
</id>
<property name="shape"/>
<property name="flavor"/>
<property name="color"/>
<union-subclass name="Apple" table="`apple`">
<property name="weight"/>
</union-subclass>
<union-subclass name="entities.Banana" table="`banana`">
<property name="length"/>
</union-subclass>
</class>
</hibernate-mapping>
好了,至此,内存中的对象与数据库元组之间的继承关系和关联关系已经下完了.