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

来源:互联网 发布:xp取消网络凭证 编辑:程序博客网 时间:2024/06/13 00:29

关联映射

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,默认值 truetable指定该列的所在数据表的表名unique指定是否为该列增加唯一约束,默认值 falsereferenceColumnName指定所参照的主键列的列名
有连接表时,需要使用 @JoinTable,来映射底层连接表的信息,支持属性如下:
name指定连接表的表名targetEntity指定关联实体的类名,默认Hibernate通过反射来判断关联实体的类名joinColumns可接受多个 @JoinColumn,用与配置连接表中的外键列信息,这些外键参照当前实体对应表的主键列inverseJoinColumns可接受多个 @JoinColumn,用与配置连接表中的外键列信息,这些外键参照关联实体对应表的主键列uniqueConstrains为链接表增加唯一约束

单向 N-1 关联

1)无连接表的单向 N-1关联
示例使用数据表,多个person可能拥有同一个address;
tableperson
columnperson_id (primary key)address_id (foreign key)person_name
tableaddresscolumnaddress_id (primary key)address_detial
Person.java
address.java
1
import javax.persistence.*;
2
3
@Entity
4
@Table(name="address")
5
public class Address {
6
    @Id @Column(name="address_id")
7
    private int id;
8
    @Column(name="addrrss_detail")
9
    private String detial;
10
    
11
    //省略所有 getter,setter方法
12
13
}

2)有关联表的单向 N-1关联
示例数据表如下:
tableperson
columnperson_id (primary key)person_name
tableaddresscolumnaddress_id (primary key)address_detial
tableperson_addresscolumnperson_id (foreign key) address_id (foreign key)id (primary key)
Person.java
address.java  同上

单向 1-N 关联

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

单向 1-1 关联

单向 1-1 关联一般使用的是无连接表的映射方式;
Person.java
1
import javax.persistence.*;
2
3
@Entity
4
@Table(name="person")
5
public class Persion {
6
    @Id @Column(name="person_id")
7
    private int id;
8
    @Column(name="person_name")
9
    private String name;
10
11
    //定义Persion实体关联的Address实体
12
    @OneToOne(targetEntity = Address.class,cascade = CascadeType.ALL)   //1-1关联,启用所有级联操作
13
    @JoinColumn(name="address_id",referencedColumnName="address_id" ,nullable = false, unique = true)    //映射外键列
14
    private Address address;
15
16
    //省略所有getter,setter方法
17
}

单向 N-N 关联

单向 N-N 关联的代码和 单向 1-N 的代码几乎完全相同,控制关系的一端只需要增加一个Set类型的属性即可;
一般 N-N 关联使用连接表的形式进行关联;
Person.java


双向 1-N 关联

大部分时候,双向 1-N 关联使用无连接表的形式进行映射即可;
1)无连接表的双向 1-N 关联
tableperson
columnperson_id (primary key)person_name
tableaddresscolumnaddress_id (primary key)person_id (foreign key)address_detial
Person.java
1
@Entity
2
@Table(name="person")
3
public class Person {
4
    @Id @Column(name="person_id")
5
    private int id;
6
    @Column(name="person_name")
7
    private String name;
8
9
    @OneToMany(targetEntity = Address.class,cascade = CascadeType.ALL,mappedBy = "person")   //1-N关联,取消本实体的关联控制
10
    private Set<Address> addresses = new HashSet<>();
11
12
    //省略所有getter,setter方法
13
}
14
Address.java
2)有连接表的双向 1-N 关联
Persion.java
1
@Entity
2
@Table(name="person")
3
public class Person {
4
    @Id @Column(name="person_id")
5
    private int id;
6
    @Column(name="person_name")
7
    private String name;
8
9
    @OneToMany(targetEntity = Address.class,cascade = CascadeType.ALL,mappedBy = "person")   //1-N关联,取消本实体的关联控制
10
    private Set<Address> addresses = new HashSet<>();
11
    //省略所有getter,setter方法
12
}
13
Address.java


双向 1-1 关联
双向 1-1 关联一般使用无连接表的形式进行映射
Person.java
1
@Entity
2
@Table(name="person")
3
public class Person {
4
    @Id @Column(name="person_id")
5
    private int id;
6
    @Column(name="person_name")
7
    private String name;
8
9
    @OneToOne(targetEntity = Address.class,cascade = CascadeType.ALL,mappedBy="person")   //1-N关联,取消本实体的关联控制
10
    private Address address;
11
    //省略所有getter,setter方法
12
}
13
address.java

双向 N-N 关联

一般双向N-N关联,使用连接表的形式映射;
Person.java
1
@Entity
2
@Table(name="person")
3
public class Person {
4
    @Id @Column(name="person_id")
5
    private int id;
6
    @Column(name="person_name")
7
    private String name;
8
9
    @ManyToMany(targetEntity = Address.class,cascade = CascadeType.ALL)  
10
    @JoinTable(name="person_address",
11
        joinColumns = @JoinColumn(name="person_id",referencedColumnName = "person_id"),
12
        inverseJoinColumns = @JoinColumn(name="address_id",referencedColumnName = "address_id"))
13
    private Set<Address> addresses = new HashSet<>();
14
    //省略所有getter,setter方法
15
}
Address.java



继承映射


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

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

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

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

Employee.java 
1
@Entity  
2
@Table(name = "employee")  
3
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)           //标注继承方式为单表方式
4
@DiscriminatorColumn(name="type",discriminatorType=DiscriminatorType.STRING)       //指定辨别者列的数据类型
5
@DiscriminatorValue(value="employee")    //指定该实体的辨别者列值
6
public class Employee {
7
    @Id
8
    @GeneratedValue(strategy = GenerationType.AUTO)
9
    @Column(name = "id")
10
    private int id;
11
12
    @Column(name = "name")
13
    private String name;
14
15
    // setters and getters
16
}
Regular_Employee.java 
Contract_Employee.java 
1
@Entity  
2
@DiscriminatorValue("contractemployee")  
3
public class Contract_Employee extends Employee {
4
    
5
    @Column(name = "pay_per_hour")
6
    private float pay_per_hour;
7
8
    @Column(name = "contract_duration")
9
    private String contract_duration;
10
    
11
    // setters and getters
12
}

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

这一种策略为每一个具体类对应一个具体的数据表,一般不会使用这种策略,因为有可能会导致数据过渡冗余;
Employee.java 
Regular_Employee.java
1
@Entity
2
@Table(name = "regularemployee")
3
@AttributeOverrides({
4
        @AttributeOverride(name = "id", column = @Column(name = "id")),
5
        @AttributeOverride(name = "name", column = @Column(name = "name")) })
6
public class Regular_Employee extends Employee {
7
8
    @Column(name = "salary")
9
    private float salary;
10
11
    @Column(name = "bonus")
12
    private int bonus;
13
14
    // setters and getters
15
16
}
Contract_Employee.java
 

连接子类的映射策略

采用这种映射策略时,父类的实体保存在父类表里,而子类的实体则由父类表和子类表共同储存,即将子类和父类共同的属性存储在父类表中;
Employee.java 
1
@Entity
2
@Table(name = "employee")
3
@Inheritance(strategy = InheritanceType.JOINED)
4
public class Employee {
5
    @Id
6
    @GeneratedValue(strategy = GenerationType.AUTO)
7
    @Column(name = "id")
8
    private int id;
9
10
    @Column(name = "name")
11
    private String name;
12
13
   // setters and getters
14
}
Regular_Employee.java 
Contract_Employee.java 
1
@Entity
2
@Table(name = "contractemployee")
3
@PrimaryKeyJoinColumn(name = "id")
4
public class Contract_Employee extends Employee {
5
6
    @Column(name = "pay_per_hour")
7
    private float pay_per_hour;
8
9
    @Column(name = "contract_duration")
10
    private String contract_duration;
11
12
    // setters and getters
13
14
}