JPA中@OneToOne、@OneToMany、@ManyToMany的對映關係
阿新 • • 發佈:2020-09-06
@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;