JPA中的單向一對多和級聯刪除
一、簡介
在本教程中,我們將探索 JPA 對相關實體之間的單向一對多關係執行級聯刪除的功能。
我們將簡要解釋級聯刪除在這種情況下的含義。然後,我們將使用一個簡單的示例來演示 JPA 如何實現預期的結果。最後,我們將對內存中的 H2 數據庫進行集成測試,以驗證該過程是否正常運行。
2. 單向一對多關係
本質上,在關係數據模型中,單向一對多關係是兩個表之間的一種關係,其中一個表在另一個表中有多個相關記錄。儘管如此,第二個表並不直接與第一個表相關。這意味著這種關係只有一個方向。
轉向 JPA,當一個實體具有對相關實體集合的引用時,可以在兩個實體之間建立單向的一對多關係。儘管如此,我們仍然無法從相關實體返回到第一個實體。通常,包含引用的實體稱為父實體,被引用的實體稱為子實體。
讓我們考慮一篇文章及其評論的示例。可以想像,一篇文章可以關聯很多條評論,但一條評論只能屬於一篇文章。這裡的Article
是父實體,而Comment
是子實體。
現在,接下來讓我們設置代表Article
和Comment
JPA 實體:
我們希望Article
在其他字段中引用其所有評論:
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
@OneToMany
private Set<Comment> comments = new HashSet<>();
//...setters and getters
}
在此示例中,我們向Article
實體添加了一個Set<Comment>
並使用@OneToMany
對其進行註釋。這向 JPA 表明了Article
和Comment
之間的單向一對多關係。 JPA 將自動生成必要的數據庫模式來支持這種關係。
接下來,讓我們定義Comment
:
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String description;
private Date date;
//...setters and getters
}
我們看不到Comment
實體中沒有對Article
的引用。因此,我們的示例表示單向關係。
3.級聯刪除
在上面的示例中,如果我們決定刪除Article
,則與其關聯的任何Comment
將保留為懸空引用或孤立對象。
為了解決這個問題,JPA 提供了一些可用於傳播刪除和清理孤立對象的屬性。
讓我們擴展Article
對像中Set<Comment>
上的@OneToMany
註釋。此註釋還可以與多個選項一起使用來自定義關係映射和行為。可用於級聯刪除的選項是cascade
。本質上cascade
允許我們定義父實體上的哪些操作(持久化、合併、刪除)應該級聯到相關的子實體。
JPA 還提供了一個選項來設置所有操作的級聯級別。這稱為CascadingType.All
。如果我們只想在刪除父實體時級聯刪除子實體,那麼我們可以使用CascadingType.Remove
。
我們將與級聯一起使用的第二個選項是orphanRemoval
。
讓我們利用 JPA 提供的這兩個選項以及我們之前建立的@OneToMany
註釋:
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Comment> comments = new HashSet<>();
//...setters and getters
}
除了使用CascadeType.All
或CascadeType.remove
之外,必須將orphanRemoval
屬性設置為 true 以確保正確刪除孤立實體。使用此屬性集,JPA 會自動從數據庫中刪除任何孤立的實體。通過使用 cascadingDelete.All 或 cascadingDelete.Remove with OrphanedRemoval=true;我們可以實現高效的數據管理,維護數據完整性,並促進自動刪除過時引用和數據庫清理。
4.集成測試
讓我們使用內存中的 H2 數據庫測試我們的級聯刪除,假設我們已經設置了服務和 JPA 存儲庫層。首先,我們將測試在刪除Article
時所有相關的Comments
是否會自動刪除。
@Test
@Transactional
public void givenAnArticleAndItsComments_whenDeleteArticle_thenCommentsDeletedAutomatically() {
Set<Comment> comments = new HashSet<>();
Article article = new Article();
article.setName("introduction to Spring");
Comment comment1 = new Comment();
comment1.setDescription("Explain types of Autowired");
comment1.setDate(Date.from(Instant.now()));
Comment comment2 = new Comment();
comment2.setDescription("Good article");
comment2.setDate(Date.from(Instant.now()
.minus(10, ChronoUnit.MINUTES)));
comments.add(comment1);
comments.add(comment2);
article.setComments(comments);
articleRepository.save(article);
List<Article> articles = articleRepository.findAll();
assertThat(articles.size()).isEqualTo(1);
Article retrivedArticle = articles.get(0);
List<Comment> fetchedComments = commentRepository.findAll();
assertThat(fetchedComments.size()).isEqualTo(2);
articleService.deleteArticle(retrivedArticle);
assertThat(articleRepository.findAll()).isEmpty();
assertThat(commentRepository.findAll()).isEmpty();
}
在這裡,我們可以看到級聯刪除操作。在我們使用articleService.deleteArticle(retrievedArticle)
刪除Article
,然後使用commentRepository.findAll()
檢索評論後,我們得到一個空列表,這意味著所有評論都已通過級聯刪除從Article
(父)到Comment
(孩子)。
5.結論
在本文中,我們簡要概述了級聯刪除,重點關注單向一對多關係。我們查看了 JPA 提供的cascade
和orphan removal
選項以及@OnetoMany
註釋,以實現數據庫中的級聯刪除和數據完整性。我們查看了CascasdeType.All
和CascadeType.Remove
以實現在刪除父實體時關聯子實體的級聯刪除。此外,我們還強調了使用OrphanRemoval
選項以確保按要求刪除數據庫記錄的重要性。
最後,我們設置了一個 Spring Boot 集成測試,以查看級聯刪除的運行情況。
一如既往,本文的完整源代碼可在 GitHub 上獲得。