iT邦幫忙

2024 iThome 鐵人賽

DAY 11
0

操作 Spring Data JPA 需要對於資料表之間的關聯與配置有一定的了解,可以幫助你在規劃資料庫及撈取資料上有很大的幫助。

後續文章會依序介紹常見的幾種關聯操作,若這邊設計一個情境開設一個電商平台,賣電玩的商品,會有一些表紀錄商品的一些資訊,我們可以藉此來探討資料表之間的關聯。主要有下面這幾張表:

  • products 商品資訊,記錄名稱 (name)、價錢 (price)、描述 (description)等資訊
  • product_details 商品細節,紀錄開發商 (developer)、發行商 (publisher)、發行日 (release_date)、支援語言 (language_support)等資訊
  • reviews 評論,紀錄評論者 (reviewer_name)、內容 (comment)、分數 (rating)
  • tags 標籤,遊戲類型及各式分類標註 (name)
  • product_tags 商品及標籤的關聯表 (多對多需要)

https://ithelp.ithome.com.tw/upload/images/20240904/201509775cEGhSRpBL.png

關聯配置相關參數介紹

關聯時必須知道誰是父實體(或稱維護方,具有 FK 可以關聯被維護 PK),誰是子實體(被維護、被參考對象)

父實體(維護方) 子實體(被維護方)
關聯 具有FK 被關聯 PK 或其他
註解應用區別 @JoinColumn() 關聯註解加入參數mappedBy

Cascade

級聯配置參數,針對父實體的資料操作,子實體會有對應自動操作

  • CascadeType.ALL:無論儲存、更新、刷新或移除,一併對被參考物件作出對應動作。
  • CascadeType.PERSIST:父實體進行儲存 (repository 的 save) 操作,自動對被關聯子實體進行儲存操作
  • CascadeType.MERGE:父實體進行更新操作時,自動對關聯的子實體執行更新操作。
  • CascadeType.REFRESH:父實體進行刷新操作時,自動對關聯的子實體執行刷新操作。
  • CascadeType.REMOVE:父實體進行刪除操作時,自動對關聯的子實體執行刪除操作。

Fetch

資料讀取配置參數,設定讀取的方式是立即或是特定條件才讀

  • FetchType.EARGE:馬上加載子實體
  • FetchType.LAZY:只有應用到子實體的屬性時才會被加載

各類關聯註解預設值參考

Relation Default
@OneToOne FetchType.EARGE
@ManyToOne FetchType.EARGE
@OneToMany FetchType.LAZY
@ManyToMany FetchType.LAZY

一對一 1 : 1

一對一的關聯可以從 products 和 product_details 這兩張表之間的關聯看到,一個商品會對應一個詳細資料,要透過 JPA 會用到 @OneToOne 註解來進行關聯,這些關聯註解都有一些參數可以設置。在這邊的範例就是 products 是父實體,product_details 是子實體。

定義父實體 products

@Entity
@Data
@Table(name = "products")
//@JsonIgnoreProperties({"productDetail"}) // 避免循環引用
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private Double price;
    private String description;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "product_detail_id", referencedColumnName = "id")
    private ProductDetails productDetails;

}

@JoinColumn 的 name 參數指的是要提供 FK 的欄位名稱,referencedColumnName 是指被關聯方的 關聯欄位(不寫預設是被關聯的主鍵PK)

定義子實體 product_details

@Entity
@Data
@Table(name = "product_details")
@JsonIgnoreProperties("product")
public class ProductDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String developer;
    private String publisher;
    private String releaseDate;
    private String languageSupport;

    @OneToOne(mappedBy = "productDetails")
    private Product product;
}

這邊需要注意如果需要雙向關聯,這邊也要在關聯要呈現的屬性上面加上 @OneToOne(mappedBy = "{父方映射關聯的欄位}"),這邊也就是指父實體放 @OneToOne 欄位的變數名 productDetails

另外補充如果雙向關聯開啟,就會出現無限循環關聯的問題,你可以想像會出現 product 關聯出 product_details 但裡面又關聯回 product 就會一直永無止境導致回傳資料混亂,有幾個方法可以解決:

  1. 改用 DTO 將需要關聯資料裝入回傳
  2. @JsonIgnore 放在被關聯欄位上面就可以忽略關聯
  3. @JsonIgnoreProperties(”{關聯欄位變數名}”) 放在實體 class 上面

實際撈回來的資料

[
    {
        "id": 1,
        "name": "最後生還者",
        "price": 59.99,
        "description": "由 Naughty Dog 開發的動作冒險遊戲。",
        "productDetails": {
            "id": 1,
            "developer": "Naughty Dog",
            "publisher": "Sony Interactive Entertainment",
            "releaseDate": "2013-06-14",
            "languageSupport": "English, Japanese, Chinese"
        }
    },
    {
        "id": 2,
        "name": "巫師3",
        "price": 49.99,
        "description": "由 CD Projekt Red 開發的開放世界角色扮演遊戲。",
        "productDetails": {
            "id": 2,
            "developer": "CD Projekt Red",
            "publisher": "CD Projekt",
            "releaseDate": "2015-05-19",
            "languageSupport": "English, Chinese, Polish"
        }
    },
    // 略......
]

以上就是基本一對一的關聯操作,下篇會來介紹一對多 (1 : N) 的操作模式。

這邊想要補充一下推薦有使用 Intellij 的 database UI ,連接好資料庫之後,對資料庫點右鍵選 Diagrams > Show Diagram 他就可以將各欄位之間的關聯狀況畫出來,還可以匯出圖片,就可以不用自己畫 ER 圖了,真的太強大 !!
https://ithelp.ithome.com.tw/upload/images/20240905/20150977RPC1MnpZSG.png


Ref:

相關文章也會同步更新我的部落格,有興趣也可以在裡面找其他的技術分享跟資訊。


上一篇
Day 10 - Spring Data JPA (2)資料庫查詢應用
下一篇
Day 12 - Spring Data JPA (4)資料庫關聯 1 : N
系列文
關於我和 Spring Boot 變成家人的那件事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Chikuwa
iT邦新手 2 級 ‧ 2024-11-18 11:02:17

嗨,Fetch Type 那邊有筆誤喔,是寫作 FetchType.EAGER

我要留言

立即登入留言