Loading

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;
posted @ 2020-09-06 17:53  未夏  阅读(1262)  评论(0编辑  收藏  举报