1. 程式人生 > 實用技巧 >JPA中@OneToOne、@OneToMany、@ManyToMany的對映關係

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例項,同時保證關係被維護端(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;