1. 程式人生 > >Hibernate學習(3) (繼承對映,配置檔案和註解版)

Hibernate學習(3) (繼承對映,配置檔案和註解版)

這篇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>

這裡寫圖片描述

好了,至此,記憶體中的物件與資料庫元組之間的繼承關係和關聯關係已經下完了.