@OneToOne、@ManyToOne的具體使用與區別

李明發表於2022-03-28

在近期的專案中我嘗試著把本應用@ManyToOne註解的地方使用@OneToOne註解,本以為進行儲存或者查詢等操作時會發生錯誤但是並沒有發生任何錯誤,所有操作都可以正常執行。
所以首先我想看一下在資料庫中生成的外來鍵是否相同。
圖片.png

  @ApiModelProperty("車輛品牌")
  @ManyToOne
  private VehicleBrand brand;

  @ApiModelProperty("車輛型別")
  @OneToOne
  private VehicleType type;

我們會發現目前在資料庫中沒有任何差別,那麼這時我就想通過檢視他們的原始碼來比較一下。

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ManyToOne {
  Class targetEntity() default void.class;

  CascadeType[] cascade() default {};

  FetchType fetch() default FetchType.EAGER;

  boolean optional() default true;
}
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OneToOne {
  Class targetEntity() default void.class;

  CascadeType[] cascade() default {};

  FetchType fetch() default FetchType.EAGER;

  boolean optional() default true;

  String mappedBy() default "";

  boolean orphanRemoval() default false;
}

通過對比我們可以發現@OneToOne只比@ManyToOne多了兩項內容,mappedBy和orphanRemoval。其餘部分完全一致。

MappedBy

只有OneToOne,OneToMany,ManyToMany上才有mappedBy屬性,ManyToOne不存在該屬性;
mappedBy標籤一定是定義在被擁有方的,他指向擁有方;

mappedBy表示宣告自己不是一對多的關係維護端,由對方來維護,是在一的一方進行宣告的。mappedBy的值應該為一的一方的表名。

mappedBy在Stack Overflow上有著更詳細的解釋
詢問者問題:mappedBy作用

Foo實體:

@ManyToMany(mappedBy = "foos")
private Set<Bar> bars
Bar實體:

@ManyToMany
private Set<Foo> foos

將其中的一個回答翻譯過來就是:

如果關聯是雙向的,則一方必須是所有者,另一方必須是反向端(即在更新關聯表中的關係值時將忽略它):
所以,具有mappedby屬性的邊是相反的邊。沒有mappedby屬性的一方是所有者。(mappedy在Foo中)
所有者側是Hibernate所檢視的哪一個關聯存在的方。因此,例如,如果在Bar的foo集合中新增foo,
hibernate將在表中插入一個新行。相反,如果在Foo的bar集合中新增一個bar,則資料庫中不會修改任何內容。

嘗試著對以上內容進行論證
假設Klass和Teacher兩個實體,他們的關係為多對多;

@Entity
public class Teacher  {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    
    @ManyToMany(mappedBy = "teacherList")
    private List<Klass> klassList = new ArrayList<>();
    
}
@Entity
public class Teacher  {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    //teacher擁有mappedBy屬性的Klass
    @ManyToMany(mappedBy = "teacherList")
    private List<Klass> klassList = new ArrayList<>();
    
}

這時從klas插入teacher是可以成功的,而從teacher插入klass是沒法成功的。

所以我嘗試著在VehicleType實體中宣告vehicle。

  @OneToOne(mappedBy = "type")
  private Vehicle vehicle;

之後我再開啟專案中相應頁面時就會發生報錯:
圖片.png
其中的 More than one row with the given identifier was found:翻譯過來就是:找到了多個具有給定識別符號的行。也就符合我們的預期。由於之前對mappedBy的認知不清晰,也就沒有要給相關聯實體增添mappedBy的習慣。

由此可以得知mappedBy不僅僅可以做到上面那樣的規定維護方的功能,還可以做到檢測儲存錯誤的功能,所以在實際的編寫中新增對應的mappedBy還是很有必要的。

至於註解中的其他屬性則是對應著jpa級聯(Cascade)操作

由於 重複性的操作十分煩瑣,尤其是在處理多個彼此關聯物件情況下,此時我們可以使用級聯(Cascade)操作。級聯 在關聯對映中是個重要的概念,指當主動方物件執行操作時,被關聯物件(被動方)是否同步執行同一操作。
由於暫時並未遇到相應的問題也就沒有深入瞭解,可以先對此有個概念,之後遇到相應問題時再來仔細瞭解。
參考文章:jpa級聯(Cascade)操作

小問題:在新建後臺實體時要保證與前臺實體的各欄位名稱保持一致,否則就會出現返回的資訊與前臺不匹配的情況。
比如前臺設定的顏色欄位為color,後臺為colour那麼前臺就接收不到後臺返還的color欄位。

相關文章