Hibernate入门(5):关联映射&继承映射
程序员文章站
2022-04-03 08:52:58
...
关联映射
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 | 指定所参照的主键列的列名 |
name | 指定连接表的表名 |
targetEntity | 指定关联实体的类名,默认Hibernate通过反射来判断关联实体的类名 |
joinColumns | 可接受多个 @JoinColumn,用与配置连接表中的外键列信息,这些外键参照当前实体对应表的主键列 |
inverseJoinColumns | 可接受多个 @JoinColumn,用与配置连接表中的外键列信息,这些外键参照关联实体对应表的主键列 |
uniqueConstrains | 为链接表增加唯一约束 |
单向 N-1 关联
1)无连接表的单向 N-1关联
示例使用数据表,多个person可能拥有同一个address;
table | person |
column |
person_id (primary key) |
address_id (foreign key) | |
person_name |
table | address |
column | address_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关联
示例数据表如下:
table | person |
column |
person_id (primary key) |
person_name |
table | address |
column | address_id (primary key) |
address_detial |
table | person_address |
column | person_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 关联
table | person |
column |
person_id (primary key) |
person_name |
table | address |
column | address_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
}