Hibernate学习(3) (继承映射,配置文件和注解版)

本文详细介绍了Hibernate中如何处理内存对象的继承关系映射到数据库,包括MappedSuperclass、Single Table、Joined Table和Table per Class四种策略。每种策略的特点和优缺点进行了分析,例如单表映射可能导致数据冗余,而 Joined Table 在数据量大时可能影响性能。文章以官方示例和注解配置文件为载体,深入讲解了各种映射方式的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇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:

DTYPEidbalanceownerinterestRateoverdraftFeecreditLimit
描述列(表明这是什么类的实例对象)主键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>

这里写图片描述

好了,至此,内存中的对象与数据库元组之间的继承关系和关联关系已经下完了.

贴上课上PPT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值