JPA中@OneToOne、@OneToMany、@ManyToMany的映射关系
@OneToOne
单向关系
假设学生和学生卡是一对一关系,那么:
学生类:
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Data
@Table(name = "student")
public class Student implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "card_id")
private Card card;
}
学生卡类:
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "card")
@Data
public class Card implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Integer number;
}
生成的数据库表如下:
- @OneToOne和@JoinColumn定义了关系维护端
- 关系维护端(Student)生成的数据库表包含外键,关系被维护端(Card)生成的数据库表不包含外键
- 当保存关系维护端(Student)前,会先保存关系被维护端(Card),同时更新外键值;
- 如果设置了级联删除,当删除关系维护端(Student)时,关系被维护端(Card)将一块被删除;否则只删除关系维护端(Student)自己
- 单向关系只保证关系维护端(Student)的每个实例有且只有一个Card实例,但无法保证一个关系被维护端(Card)的实例只被一个关系维护端(Student)的实例引用即一张学生卡可能对应多个人
- 当删除关系被维护端(Card)时,程序报错
Cannot delete or update a parent row: a foreign key constraint fails
,是由于被维护端的数据被通过外键引用无法删除
双向
如果更改为双向关系,只需改动关系被维护端:
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "card")
@Data
public class Card implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Integer number;
@OneToOne(mappedBy = "card")
private Student student;
}
- @OneToOne的
mappedBy
属性定义了关系被维护端,表示主动让出放弃维护权,由当前被标注字段所属类型(Student类)的类属性名(card属性名)负责维护关系,类属性名是当前类的实例或实例集合 - 关系维护端(Student)生成的数据库表包含外键,关系被维护端(Card)生成的数据库表不包含外键
- 当保存关系维护端(Student)前,会先保存关系被维护端(Card),同时更新外键值;如果通过关系被维护端来保存关系维护端实例,关系维护端实例会保存,但外键值为空或不更新
- 双向关系保证关系维护端(Student)的每个实例有且只有一个Card实例,同时保证关系被维护端(Card)的实例只被一个关系维护端(Student)的实例引用
@OneToMany
单向
假设一个学校有多个教师,那么:
学校类:
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
@Entity
@Table(name = "school")
@Data
public class School implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "schoolIDid")
private List<Teacher> teacherList;
}
教师类:
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
@Entity
@Table(name = "teacher")
@Data
public class Teacher implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@ManyToOne
@JoinColumn(name = "schoolIDid")
private School school;
}
生成的数据库表如下:
在一对多关系中,多的一方是关系维护端即@ManyToOne定义关系维护端,@OneToMany定义关系被维护端
关系维护端生成的数据库表包含外键,关系被维护端生成的数据表不包含外键。
关系维护端负责外键记录的更新,关系被维护端没有权利更新外键字段
@JoinColumn作用:
- 阻止生成中间表。如果想阻止生成中间表,关系被维护端(一的一方)必须标注@JoinColumn
- 指定外键名。默认外键名为关系被维护端的表名加下划线加id,如果指定外键名则关系维护端和关系被维护端都必须标注@JoinColumn
如果关系被维护端没有使用@JoinColumn,那么数据库会生成中间表:
不同于一对一关系,关系维护端保存前必须先维护关系被维护端,否则报错
双向
只须在关系被维护端的@OneToMany注解中添加mappedBy
属性
@MonyToMony
单向
一对多和一对一关系,可以用外键实现;多对多关系只能通过中间表进行映射。
假设一个学生有多个老师,一个老师有多个学生,那么:
关系维护端注解如下:
@ManyToMany (cascade = CascadeType.REFRESH)
@JoinTable (//关联表
name = "student_teacher" , //关联表名
inverseJoinColumns = @JoinColumn (name = "teacher_id" ),//被维护端外键
joinColumns = @JoinColumn (name = "student_id" ) //维护端外键
)
private Set<Teacher> teachers;
关系被维护端注解如下:
@ManyToMany (fetch = FetchType.LAZY)
@JoinTable (//关联表
name = "student_teacher" , //关联表名
inverseJoinColumns = @JoinColumn (name = "student_id" ),//被维护端外键
joinColumns = @JoinColumn (name = "teacher_id" ) //维护端外键
)
private Set<Student> students;
- 关系维护端删除时,如果中间表存在些纪录的关联信息,则会删除该关联信息
- 关系被维护端删除时,如果中间表存在些纪录的关联信息,则会删除失败
关联表名 = 主表表名+_下划线+从表表名
joinColumns属性=当前实体类数据库表名+_下划线+id
双向
关系维护端注解不变,关系被维护端注解更改为:
@ManyToMany (fetch = FetchType.LAZY,
mappedBy = "teachers", //通过维护端的属性关联
cascade = CascadeType.REFRESH
)
private Set<Student> students;