Hibernate入门(5):关联映射&继承映射

关联映射

1、除了关系型数据库本身提供的基于关系的关联映射,Hibernate 也提供了面向对象的关联映射支持,这些关联关系大大简化了持久层数据的访问;
2、Hibernate提供的关联关系可以分为以下2种, 可以根据需求选择关联形式;
单向关联:只能从一端访问另一端;
双向关联:两端都可以相互访问;

3、单向关联包含以下4种关系:
1-1(一对一),1-N(一对多),N-1(多对一),N-N(多对多); 
4、双向关联包含以下3种关系(双向关联中1-N,N-1完全相同 ):
1-1,1-N,N-N;

5、对于这些关联关系,hibernate 提供了以下4个注解支持:
@OneToOne:1-1关系
@OneToMore:1-N关系
@MoreToOne:N-1关系
@MoreToMore:N-N关系
这些注解支持的常用属性如下:
cascade指定对于关联主体的级联策略,不设置时,不启用级联,可选值如下:
CascadeType.ALL:将所有的持久化操作级联到关联实体;
CascadeType.MERGE:将merge操作级联到关联实体;
CascadeType.PRESIST :将presist操作级联到关联实体;
CascadeType.REFRESH :将refresh操作级联到关联实体;
CascadeType.REMOVE :将remove操作级联到关联实体;
fetch指定抓取关联实体的策略,可选值如下:
FetchType.EAGER:立即抓取
FetchType.:LAZY:延迟抓取(默认值)
mappedBy指定关联实体中那个属性值可以被引用到当前实体,值为关联实体的某个属性名;
当@OneToMany @ManyToMany 指定了该属性后,表明当前实体不能控制关联关系;
当某一段使用mappedBy放弃控制权后,该一段不能使用@JoinColumn,@JoinTable
orphanRemoval设置是否删除“孤儿”实体,如果某个实体所关联的父实体不存在,该实体为孤儿实体
tragetEntity该属性关联实体的类名,默认情况下 hibernate 会通过反射来判断关联实体的类名

6、对于1-N 关联关系,在数据库建模角度来讲,这两个关联的表存在基于外键分别为主表、从表的约束关系,hibernate有2种方式来进行表示:
①组件映射:将从表记录映射为持久化类的组件,参见 04. Hibernate 集合映射&组件映射,这种方式下 Hibernate   默认启用级联操作,组件的声明周期依赖于父对象:父对象保存时,组件被保存,父对象被删除时,组件被删除;
②关联映射:将从表记录映射为持久化实体,这种方式下 hibernate   默认不开启级联操作,从表实体的拥有自己的声明周期,从而允许其他实体共享对其的引用;

7、Hibernate 支持有连接表和无连接表的关联映射;
无连接表时,需要使用   @JoinColumn ,来映射外键列,支持属性如下:
name指定外键列列名
columnDefinition使用指定的SQL片段创建外键列
nullable指定该列是否允许为null,默认值 true
table指定该列的所在数据表的表名
unique指定是否为该列增加唯一约束,默认值 false
referenceColumnName指定所参照的主键列的列名
有连接表时,需要使用   @JoinTable,来映射底层连接表的信息,支持属性如下:
name指定连接表的表名
targetEntity指定关联实体的类名,默认Hibernate通过反射来判断关联实体的类名
joinColumns可接受多个 @JoinColumn,用与配置连接表中的外键列信息,这些外键参照当前实体对应表的主键列
inverseJoinColumns可接受多个 @JoinColumn,用与配置连接表中的外键列信息,这些外键参照关联实体对应表的主键列
uniqueConstrains为链接表增加唯一约束

单向 N-1 关联

1)无连接表的单向 N-1关联
示例使用数据表,多个person 可能拥有同一个address;
tableperson

column
person_id (primary key)
address_id (foreign key)
person_name
tableaddress
columnaddress_id (primary key)
address_detial
Person .java
import javax.persistence.*;

@Entity
@Table(name="persion")
public class Person {
    @Id @Column(name="person_id")
    private int id;
    @Column(name="persion_name")
    private String name;

    //定义Persion实体关联的Address实体
    @ManyToOne(targetEntity = Address.class,cascade = CascadeType.ALL)   //N-1关联,启用所有级联操作
    @JoinColumn(name="address_id",referencedColumnName="address_id" ,nullable = false)    //映射外键列
    private Address address;

    //省略所有getter,setter方法
}

address.java
import javax.persistence.*;

@Entity
@Table(name="address")
public class Address {
    @Id @Column(name="address_id")
    private int id;
    @Column(name="addrrss_detail")
    private String detial;
    
    //省略所有 getter,setter方法

}
2)有关联表的单向 N-1关联
示例数据表如下:
tableperson

column
person_id (primary key)
person_name
tableaddress
columnaddress_id (primary key)
address_detial
tableperson_address
columnperson_id (foreign key) 
address_id (foreign key)
id (primary key)
Person .java
import javax.persistence.*;
@Entity
@Table(name="person")
public class Persion {
    @Id @Column(name="person_id")
    private int id;
    @Column(name="person_name")
    private String name;

    //定义Persion实体关联的Address实体
    @ManyToOne(targetEntity = Address.class,cascade = CascadeType.ALL)   //N-1关联,启用所有级联操作
    //映射连接表
    @JoinTable(name="person_address",             //指定连接表名称
        joinColumns = @JoinColumn(name="person_id",referencedColumnName = "person_id",unique=true),    //连接表外键对当前实体主键的参照
        inverseJoinColumns = @JoinColumn(name="address_id",referencedColumnName = "address_id"))  //连接表外键对关联实体主键的参照
    private Address address;

    //省略所有getter,setter方法
}

address.java  同上

单向 1-N 关联

对于 1-N 关联,Hibernate 推荐使用双向关联,同时不让1的一端控制关联关系,而使用N的一端控制关联关系,当程序不得不使用使用单向的 1-N,优先采用连接表的 1-N 关联;

单向 1-1 关联

单向 1-1 关联一般使用的是无连接表的映射方式;
Person .java
import javax.persistence.*;

@Entity
@Table(name="person")
public class Persion {
    @Id @Column(name="person_id")
    private int id;
    @Column(name="person_name")
    private String name;

    //定义Persion实体关联的Address实体
    @OneToOne(targetEntity = Address.class,cascade = CascadeType.ALL)   //1-1关联,启用所有级联操作
    @JoinColumn(name="address_id",referencedColumnName="address_id" ,nullable = false, unique = true)    //映射外键列
    private Address address;

    //省略所有getter,setter方法
}

单向 N-N 关联

单向 N-N 关联的代码和 单向 1-N 的代码几乎完全相同,控制关系的一端只需要增加一个Set类型的属性即可;
一般 N-N 关联使用连接表的形式进行关联;
Person .java
@Entity
@Table(name="person")
public class Persion {
    @Id @Column(name="person_id")
    private int id;
    @Column(name="person_name")
    private String name;

    //定义Persion实体关联的Address实体
    @ManyToMany(targetEntity = Address.class,cascade = CascadeType.ALL)   //N-N关联,启用所有级联操作
    //映射连接表
    @JoinTable(name="person_address",             //指定连接表名称
        joinColumns = @JoinColumn(name="person_id",referencedColumnName = "person_id"),    //连接表外键对当前实体主键的参照
        inverseJoinColumns = @JoinColumn(name="address_id",referencedColumnName = "address_id"))  //连接表外键对关联实体主键的参照
    private Set<Address> addresses = new HashSet<>();

    //省略所有getter,setter方法
}

双向 1-N 关联

大部分时候,双向 1-N 关联使用无连接表的形式进行映射即可;
1)无连接表的双向 1-N 关联
tableperson

column
person_id (primary key)
person_name
tableaddress
columnaddress_id (primary key)
person_id (foreign key)
address_detial
Person.java
@Entity
@Table(name="person")
public class Person {
    @Id @Column(name="person_id")
    private int id;
    @Column(name="person_name")
    private String name;

    @OneToMany(targetEntity = Address.class,cascade = CascadeType.ALL,mappedBy = "person")   //1-N关联,取消本实体的关联控制
    private Set<Address> addresses = new HashSet<>();

    //省略所有getter,setter方法
}
Address.java
@Entity
@Table(name="address")
public class Address {
    @Id @Column(name="address_id")
    private int id;
    @Column(name="addrrss_detail")
    private String detial;

    @ManyToOne(targetEntity = Person.class,cascade=CascadeType.ALL)
    @JoinColumn(name="person_id",referencedColumnName = "person_id",nullable = false)
    private Person person;

    //省略所有 getter,setter方法

}

2)有连接表的双向 1-N 关联
Persion.java
@Entity
@Table(name="person")
public class Person {
    @Id @Column(name="person_id")
    private int id;
    @Column(name="person_name")
    private String name;

    @OneToMany(targetEntity = Address.class,cascade = CascadeType.ALL,mappedBy = "person")   //1-N关联,取消本实体的关联控制
    private Set<Address> addresses = new HashSet<>();
    //省略所有getter,setter方法
}
Address.java
@Entity
@Table(name="address")
public class Address {
    @Id @Column(name="address_id")
    private int id;
    @Column(name="addrrss_detail")
    private String detial;

    @ManyToOne(targetEntity = Person.class,cascade=CascadeType.ALL)
    @JoinTable(name="person_address",
        joinColumns = @JoinColumn(name="adddress_id",referencedColumnName = "address_id",unique=true),
        inverseJoinColumns = @JoinColumn(name="person_id",referencedColumnName = "person_id"))
    private Person person;
    
    //省略所有 getter,setter方法

}


双向 1-1 关联
双向 1-1 关联一般使用无连接表的形式进行映射
Person.java
@Entity
@Table(name="person")
public class Person {
    @Id @Column(name="person_id")
    private int id;
    @Column(name="person_name")
    private String name;

    @OneToOne(targetEntity = Address.class,cascade = CascadeType.ALL,mappedBy="person")   //1-N关联,取消本实体的关联控制
    private Address address;
    //省略所有getter,setter方法
}
address.java
@Entity
@Table(name="address")
public class Address {
    @Id @Column(name="address_id")
    private int id;
    @Column(name="addrrss_detail")
    private String detial;

    @ManyToOne(targetEntity = Person.class,cascade=CascadeType.ALL)
    @JoinColumn(name="person_id",referencedColumnName = "person_id",unique=true)
    private Person person;

    //省略所有 getter,setter方法

}

双向 N-N 关联

一般双向N-N关联,使用连接表的形式映射;
Person.java
@Entity
@Table(name="address")
public class Address {
    @Id @Column(name="address_id")
    private int id;
    @Column(name="addrrss_detail")
    private String detial;

    @ManyToOne(targetEntity = Person.class,cascade=CascadeType.ALL)
    @JoinColumn(name="person_id",referencedColumnName = "person_id",unique=true)
    private Person person;

    //省略所有 getter,setter方法

}
Address.java
@Entity
@Table(name="address")
public class Address {
    @Id @Column(name="address_id")
    private int id;
    @Column(name="addrrss_detail")
    private String detial;

    @ManyToOne(targetEntity = Person.class,cascade=CascadeType.ALL)
    @JoinTable(name="person_address",
        joinColumns = @JoinColumn(name="adddress_id",referencedColumnName = "address_id"),
        inverseJoinColumns = @JoinColumn(name="person_id",referencedColumnName = "person_id"))
    private Set<Person> persons = new HashSet<>();
    //省略所有 getter,setter方法

}



继承映射


hibernate 支持将继承层次结构与数据表进行映射,其定义了3种继承映射的策略:
① 每个类层次对应一张表的映射策略;
② 每个具体类对应一张表的映射策略;
③ 连接子类的映射策略;

以下使用图示的继承层次进行示例说明:

每个类层次对应一张表的映射策略

这是Hibernate默认的继承映射策略,在这种策略下,整个继承层次结构都储存在一张表中,如上继承层次中 Employee,Regular_Emplee,Constract_Employee 3个实体都储存在同一张表中;
使用这种策略时,Hibernate 会为该表额外创建一列,该列用于区分每行记录为哪一个类的实例,该列为辨别者列(discriminiator), 使用@DiscriminatorColumn配置辨别者列,

Employee.java
@Entity  
@Table(name = "employee")  
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)           //标注继承方式为单表方式
@DiscriminatorColumn(name="type",discriminatorType=DiscriminatorType.STRING)       //指定辨别者列的数据类型
@DiscriminatorValue(value="employee")    //指定该实体的辨别者列值
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @Column(name = "name")
    private String name;

    // setters and getters
}
Regular_Employee.java
@Entity  
@DiscriminatorValue("regularemployee")  
public class Regular_Employee extends Employee {

    @Column(name = "salary")
    private float salary;

    @Column(name = "bonus")
    private int bonus;
    
    // setters and getters
}
Contract_Employee.java
@Entity  
@DiscriminatorValue("contractemployee")  
public class Contract_Employee extends Employee {
    
    @Column(name = "pay_per_hour")
    private float pay_per_hour;

    @Column(name = "contract_duration")
    private String contract_duration;
    
    // setters and getters
}

每个具体类对应一张表的映射策略

这一种策略为每一个具体类对应一个具体的数据表,一般不会使用这种策略,因为有可能会导致数据过渡冗余;
Employee.java
@Entity
@Table(name = "employee")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @Column(name = "name")
    private String name;

    // setters and getters

}

Regular_Employee.java
@Entity
@Table(name = "regularemployee")
@AttributeOverrides({
        @AttributeOverride(name = "id", column = @Column(name = "id")),
        @AttributeOverride(name = "name", column = @Column(name = "name")) })
public class Regular_Employee extends Employee {

    @Column(name = "salary")
    private float salary;

    @Column(name = "bonus")
    private int bonus;

    // setters and getters

}
Contract_Employee.java
@Entity
@Table(name = "contractemployee")
@AttributeOverrides({
        @AttributeOverride(name = "id", column = @Column(name = "id")),
        @AttributeOverride(name = "name", column = @Column(name = "name")) })
public class Contract_Employee extends Employee {

    @Column(name = "pay_per_hour")
    private float pay_per_hour;

    @Column(name = "contract_duration")
    private String contract_duration;

     // setters and getters
}


连接子类的映射策略

采用这种映射策略时,父类的实体保存在父类表里,而子类的实体则由父类表和子类表共同储存,即将子类和父类共同的属性存储在父类表中;
Employee.java
@Entity
@Table(name = "employee")
@Inheritance(strategy = InheritanceType.JOINED)
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @Column(name = "name")
    private String name;

   // setters and getters
}
Regular_Employee.java
@Entity
@Table(name = "regularemployee")
@PrimaryKeyJoinColumn(name = "id")
public class Regular_Employee extends Employee {
    @Column(name = "salary")
    private float salary;
     
    @Column(name = "bonus")
    private int bonus;
    
   // setters and getters
}
Contract_Employee.java
@Entity
@Table(name = "contractemployee")
@PrimaryKeyJoinColumn(name = "id")
public class Contract_Employee extends Employee {

    @Column(name = "pay_per_hour")
    private float pay_per_hour;

    @Column(name = "contract_duration")
    private String contract_duration;

    // setters and getters

}




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值