日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jpa和hibernate_JPA和Hibernate级联类型的初学者指南

發布時間:2023/12/3 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jpa和hibernate_JPA和Hibernate级联类型的初学者指南 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

jpa和hibernate

介紹

JPA將實體狀態轉換轉換為數據庫DML語句。 由于對實體圖進行操作很常見,因此JPA允許我們將實體狀態更改從父級傳播到子級 。

通過CascadeType映射配置此行為。

JPA與Hibernate級聯類型

Hibernate支持所有JPA級聯類型和一些其他舊式級聯樣式。 下表繪制了JPA級聯類型與其等效的Hibernate本機API之間的關聯:

JPA EntityManager操作 JPA CascadeType Hibernate本機會話操作 Hibernate原生CascadeType 事件監聽器
分離(實體) 分離 逐出(實體) 分離或

EVICT
默認驅逐事件偵聽器
合并(實體) 合并 合并(實體) 合并 默認合并事件監聽器
堅持(實體) 堅持 堅持(實體) 堅持 默認的持久事件監聽器
刷新(實體) 刷新 刷新(實體) 刷新 默認刷新事件監聽器
刪除(實體) 去掉 刪除(實體) 刪除或刪除 默認刪除事件監聽器
saveOrUpdate(實體) SAVE_UPDATE 默認的保存或更新事件監聽器
復制(實體,復制模式) 復制 默認復制事件監聽器
鎖(實體,lockModeType) buildLockRequest(實體,lockOptions) 默認鎖定事件監聽器
以上所有EntityManager方法 所有 以上所有的Hibernate Session方法 所有

從該表可以得出以下結論:

  • 在JPA EntityManager或Hibernate Session上調用persist , merge或refresh沒有什么區別。
  • JPA的remove和detach調用被委托給Hibernate Delete和逐出本機操作。
  • 只有Hibernate支持復制和saveOrUpdate 。 盡管復制對于某些非常特定的情況很有用(當確切的實體狀態需要在兩個不同的數據源之間進行鏡像時),但持久 合并合并始終是比本機saveOrUpdate操作更好的替代方法。將持久對象用于TRANSIENT實體,并為已分離實體進行合并。saveOrUpdate的缺點(將分離的實體快照傳遞給已經管理該實體的會話時 )導致了合并操作的前身:現已不存在的saveOrUpdateCopy操作。
  • JPA鎖定方法與Hibernate鎖定請求方法具有相同的行為。
  • JPA CascadeType.ALL不僅適用于EntityManager狀態更改操作,而且還適用于所有Hibernate CascadeTypes 。因此,如果將關聯與CascadeType.ALL映射,您仍然可以級聯Hibernate特定事件。 例如,即使JPA沒有定義LOCK CascadeType ,您也可以級聯JPA鎖定操作(盡管它的行為就像是重新附加,而不是實際的鎖定請求傳播)。

級聯最佳做法

級聯僅對父級 - 子級關聯有意義( 父級實體狀態轉換級聯到其子級實體)。 從孩子級聯到父級不是很有用,通常,這是映射代碼的味道。

接下來,我將采取分析所有JPA 家長的級聯行為- 子關聯。

一對一

最常見的一對一雙向關聯如下所示:

@Entity public class Post {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;private String name;@OneToOne(mappedBy = "post",cascade = CascadeType.ALL, orphanRemoval = true)private PostDetails details;public Long getId() {return id;}public PostDetails getDetails() {return details;}public String getName() {return name;}public void setName(String name) {this.name = name;}public void addDetails(PostDetails details) {this.details = details;details.setPost(this);}public void removeDetails() {if (details != null) {details.setPost(null);}this.details = null;} }@Entity public class PostDetails {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(name = "created_on")@Temporal(TemporalType.TIMESTAMP)private Date createdOn = new Date();private boolean visible;@OneToOne@PrimaryKeyJoinColumnprivate Post post;public Long getId() {return id;}public void setVisible(boolean visible) {this.visible = visible;}public void setPost(Post post) {this.post = post;} }

Post實體扮演Parent角色,而PostDetails是Child 。

雙向關聯應始終在兩側進行更新,因此父級側應包含addChild和removeChild組合。 這些方法確保我們始終同步關聯的雙方,以避免對象或關系數據損壞問題。

在這種特殊情況下, CascadeType.ALL和孤立刪除是有意義的,因為PostDetails生命周期綁定到其Post Parent實體的生命周期。

進行一對一的持久化操作

CascadeType.PERSIST與CascadeType.ALL配置一起提供,因此我們僅需持久化Post實體,并且關聯的PostDetails實體也將持久化:

Post post = new Post(); post.setName("Hibernate Master Class");PostDetails details = new PostDetails();post.addDetails(details);session.persist(post);

生成以下輸出:

INSERT INTO post(id, NAME) VALUES (DEFAULT, Hibernate Master Class'')insert into PostDetails (id, created_on, visible) values (default, '2015-03-03 10:17:19.14', false)

級聯一對一合并操作

CascadeType.MERGE繼承自CascadeType.ALL設置,因此我們只需要合并Post實體,并且關聯的PostDetails也將合并:

Post post = newPost(); post.setName("Hibernate Master Class Training Material"); post.getDetails().setVisible(true);doInTransaction(session -> {session.merge(post); });

合并操作生成以下輸出:

SELECT onetooneca0_.id AS id1_3_1_,onetooneca0_.NAME AS name2_3_1_,onetooneca1_.id AS id1_4_0_,onetooneca1_.created_on AS created_2_4_0_,onetooneca1_.visible AS visible3_4_0_ FROM post onetooneca0_ LEFT OUTER JOIN postdetails onetooneca1_ ON onetooneca0_.id = onetooneca1_.id WHERE onetooneca0_.id = 1UPDATE postdetails SET created_on = '2015-03-03 10:20:53.874', visible = true WHERE id = 1UPDATE post SET NAME = 'Hibernate Master Class Training Material' WHERE id = 1

級聯一對一刪除操作

CascadeType.REMOVE也繼承自CascadeType.ALL配置,因此Post實體的刪除也會觸發PostDetails實體的刪除:

Post post = newPost();doInTransaction(session -> {session.delete(post); });

生成以下輸出:

delete from PostDetails where id = 1 delete from Post where id = 1

一對一刪除孤立級聯操作

如果一個孩子實體從母公司分離,兒童外鍵設置為NULL。 如果我們也要刪除“ 子行”,則必須使用孤立刪除支持。

doInTransaction(session -> {Post post = (Post) session.get(Post.class, 1L);post.removeDetails(); });

除去孤兒將生成以下輸出:

SELECT onetooneca0_.id AS id1_3_0_,onetooneca0_.NAME AS name2_3_0_,onetooneca1_.id AS id1_4_1_,onetooneca1_.created_on AS created_2_4_1_,onetooneca1_.visible AS visible3_4_1_ FROM post onetooneca0_ LEFT OUTER JOIN postdetails onetooneca1_ON onetooneca0_.id = onetooneca1_.id WHERE onetooneca0_.id = 1delete from PostDetails where id = 1

單向一對一關聯

大多數情況下, 父實體是反方(如的mappedBy), 兒童 controling通過它的外鍵關聯。 但是級聯不限于雙向關聯,我們還可以將其用于單向關系:

@Entity public class Commit {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;private String comment;@OneToOne(cascade = CascadeType.ALL)@JoinTable(name = "Branch_Merge_Commit",joinColumns = @JoinColumn(name = "commit_id", referencedColumnName = "id"),inverseJoinColumns = @JoinColumn(name = "branch_merge_id", referencedColumnName = "id"))private BranchMerge branchMerge;public Commit() {}public Commit(String comment) {this.comment = comment;}public Long getId() {return id;}public void addBranchMerge(String fromBranch, String toBranch) {this.branchMerge = new BranchMerge(fromBranch, toBranch);}public void removeBranchMerge() {this.branchMerge = null;} }@Entity public class BranchMerge {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;private String fromBranch;private String toBranch;public BranchMerge() {}public BranchMerge(String fromBranch, String toBranch) {this.fromBranch = fromBranch;this.toBranch = toBranch;}public Long getId() {return id;} }

層疊在于傳播父實體狀態過渡到一個或多個兒童的實體,它可用于單向和雙向關聯。

一對多

最常見的父 - 子關聯由一到多和多到一的關系,其中級聯是只對一個一對多側有用:

@Entity public class Post {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;private String name;@OneToMany(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true)private List<Comment> comments = new ArrayList<>();public void setName(String name) {this.name = name;}public List<Comment> getComments() {return comments;}public void addComment(Comment comment) {comments.add(comment);comment.setPost(this);}public void removeComment(Comment comment) {comment.setPost(null);this.comments.remove(comment);} }@Entity public class Comment {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@ManyToOneprivate Post post;private String review;public void setPost(Post post) {this.post = post;}public String getReview() {return review;}public void setReview(String review) {this.review = review;} }

像一對一示例一樣, CascadeType.ALL和孤立刪除是合適的,因為Comment生命周期綁定到其Post Parent實體的生命周期。

級聯一對多持久化操作

我們只需保留Post實體,所有相關的Comment實體也將保留:

Post post = new Post(); post.setName("Hibernate Master Class");Comment comment1 = new Comment(); comment1.setReview("Good post!"); Comment comment2 = new Comment(); comment2.setReview("Nice post!");post.addComment(comment1); post.addComment(comment2);session.persist(post);

持久操作將生成以下輸出:

insert into Post (id, name) values (default, 'Hibernate Master Class')insert into Comment (id, post_id, review) values (default, 1, 'Good post!')insert into Comment (id, post_id, review) values (default, 1, 'Nice post!')

級聯一對多合并操作

合并Post實體也將合并所有Comment實體:

Post post = newPost(); post.setName("Hibernate Master Class Training Material");post.getComments().stream().filter(comment -> comment.getReview().toLowerCase().contains("nice")).findAny().ifPresent(comment -> comment.setReview("Keep up the good work!") );doInTransaction(session -> {session.merge(post); });

生成以下輸出:

SELECT onetomanyc0_.id AS id1_1_1_,onetomanyc0_.NAME AS name2_1_1_,comments1_.post_id AS post_id3_1_3_,comments1_.id AS id1_0_3_,comments1_.id AS id1_0_0_,comments1_.post_id AS post_id3_0_0_,comments1_.review AS review2_0_0_ FROM post onetomanyc0_ LEFT OUTER JOIN comment comments1_ON onetomanyc0_.id = comments1_.post_id WHERE onetomanyc0_.id = 1update Post set name = 'Hibernate Master Class Training Material' where id = 1update Comment set post_id = 1, review='Keep up the good work!' where id = 2

級聯一對多刪除操作

刪除Post實體后,關聯的Comment實體也將被刪除:

Post post = newPost();doInTransaction(session -> {session.delete(post); });

生成以下輸出:

delete from Comment where id = 1 delete from Comment where id = 2 delete from Post where id = 1

一對多刪除孤立級聯操作

移除孤兒使我們可以在其父級不再引用子級實體時將其刪除:

newPost();doInTransaction(session -> {Post post = (Post) session.createQuery("select p " +"from Post p " +"join fetch p.comments " +"where p.id = :id").setParameter("id", 1L).uniqueResult();post.removeComment(post.getComments().get(0)); });

正如我們在以下輸出中看到的,評論已刪除:

SELECT onetomanyc0_.id AS id1_1_0_,comments1_.id AS id1_0_1_,onetomanyc0_.NAME AS name2_1_0_,comments1_.post_id AS post_id3_0_1_,comments1_.review AS review2_0_1_,comments1_.post_id AS post_id3_1_0__,comments1_.id AS id1_0_0__ FROM post onetomanyc0_ INNER JOIN comment comments1_ON onetomanyc0_.id = comments1_.post_id WHERE onetomanyc0_.id = 1delete from Comment where id = 1

多對多

多對多關系非常棘手,因為此關聯的每一方都扮演“ 父母”和“ 孩子”角色。 盡管如此,我們仍可以從一側傳播我們想要傳播實體狀態變化的位置。

我們不應該默認使用CascadeType.ALL ,因為CascadeTpe.REMOVE最終可能會刪除比我們期望的更多的東西(您很快就會發現):

@Entity public class Author {@Id@GeneratedValue(strategy=GenerationType.AUTO)private Long id;@Column(name = "full_name", nullable = false)private String fullName;@ManyToMany(mappedBy = "authors", cascade = {CascadeType.PERSIST, CascadeType.MERGE})private List<Book> books = new ArrayList<>();private Author() {}public Author(String fullName) {this.fullName = fullName;}public Long getId() {return id;}public void addBook(Book book) {books.add(book);book.authors.add(this);}public void removeBook(Book book) {books.remove(book);book.authors.remove(this);}public void remove() {for(Book book : new ArrayList<>(books)) {removeBook(book);}} }@Entity public class Book {@Id@GeneratedValue(strategy=GenerationType.AUTO)private Long id;@Column(name = "title", nullable = false)private String title;@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})@JoinTable(name = "Book_Author",joinColumns = {@JoinColumn(name = "book_id", referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "author_id", referencedColumnName = "id")})private List<Author> authors = new ArrayList<>();private Book() {}public Book(String title) {this.title = title;} }

級聯多對多持久操作

堅持作者實體也將保留書籍 :

Author _John_Smith = new Author("John Smith"); Author _Michelle_Diangello = new Author("Michelle Diangello"); Author _Mark_Armstrong = new Author("Mark Armstrong");Book _Day_Dreaming = new Book("Day Dreaming"); Book _Day_Dreaming_2nd = new Book("Day Dreaming, Second Edition");_John_Smith.addBook(_Day_Dreaming); _Michelle_Diangello.addBook(_Day_Dreaming);_John_Smith.addBook(_Day_Dreaming_2nd); _Michelle_Diangello.addBook(_Day_Dreaming_2nd); _Mark_Armstrong.addBook(_Day_Dreaming_2nd);session.persist(_John_Smith); session.persist(_Michelle_Diangello); session.persist(_Mark_Armstrong);

Book和Book_Author行與Authors一起插入:

insert into Author (id, full_name) values (default, 'John Smith')insert into Book (id, title) values (default, 'Day Dreaming')insert into Author (id, full_name) values (default, 'Michelle Diangello')insert into Book (id, title) values (default, 'Day Dreaming, Second Edition')insert into Author (id, full_name) values (default, 'Mark Armstrong')insert into Book_Author (book_id, author_id) values (1, 1) insert into Book_Author (book_id, author_id) values (1, 2) insert into Book_Author (book_id, author_id) values (2, 1) insert into Book_Author (book_id, author_id) values (2, 2) insert into Book_Author (book_id, author_id) values (3, 1)

解除多對多關聯的一側

要刪除Author ,我們需要取消關聯屬于可移動實體的所有Book_Author關系:

doInTransaction(session -> {Author _Mark_Armstrong =getByName(session, "Mark Armstrong");_Mark_Armstrong.remove();session.delete(_Mark_Armstrong); });

該用例生成以下輸出:

SELECT manytomany0_.id AS id1_0_0_,manytomany2_.id AS id1_1_1_,manytomany0_.full_name AS full_nam2_0_0_,manytomany2_.title AS title2_1_1_,books1_.author_id AS author_i2_0_0__,books1_.book_id AS book_id1_2_0__ FROM author manytomany0_ INNER JOIN book_author books1_ON manytomany0_.id = books1_.author_id INNER JOIN book manytomany2_ON books1_.book_id = manytomany2_.id WHERE manytomany0_.full_name = 'Mark Armstrong'SELECT books0_.author_id AS author_i2_0_0_,books0_.book_id AS book_id1_2_0_,manytomany1_.id AS id1_1_1_,manytomany1_.title AS title2_1_1_ FROM book_author books0_ INNER JOIN book manytomany1_ON books0_.book_id = manytomany1_.id WHERE books0_.author_id = 2delete from Book_Author where book_id = 2insert into Book_Author (book_id, author_id) values (2, 1) insert into Book_Author (book_id, author_id) values (2, 2)delete from Author where id = 3

多對多關聯會生成太多冗余SQL語句,并且經常很難調整它們。 接下來,我將演示多對多CascadeType.REMOVE隱藏的危險。

多對多CascadeType.REMOVE陷阱

多對多CascadeType.ALL是另一個代碼異味,我在查看代碼時經常碰到。 所述CascadeType.REMOVE使用CascadeType.ALL時自動繼承,但實體去除不僅應用到鏈接表,但對關聯的另一側為好。

讓我們將Author實體書籍多對多關聯更改為使用CascadeType.ALL代替:

@ManyToMany(mappedBy = "authors", cascade = CascadeType.ALL) private List<Book> books = new ArrayList<>();

刪除一位作者時 :

doInTransaction(session -> {Author _Mark_Armstrong = getByName(session, "Mark Armstrong");session.delete(_Mark_Armstrong);Author _John_Smith = getByName(session, "John Smith");assertEquals(1, _John_Smith.books.size()); });

屬于已刪除作者的所有圖書都將被刪除,即使我們仍與已刪除圖書相關聯的其他作者也是如此:

SELECT manytomany0_.id AS id1_0_,manytomany0_.full_name AS full_nam2_0_ FROM author manytomany0_ WHERE manytomany0_.full_name = 'Mark Armstrong' SELECT books0_.author_id AS author_i2_0_0_,books0_.book_id AS book_id1_2_0_,manytomany1_.id AS id1_1_1_,manytomany1_.title AS title2_1_1_ FROM book_author books0_ INNER JOIN book manytomany1_ ON books0_.book_id = manytomany1_.id WHERE books0_.author_id = 3 delete from Book_Author where book_id=2 delete from Book where id=2 delete from Author where id=3

通常,此行為與業務邏輯期望不符,僅在首次刪除實體時才發現。

如果我們也將CascadeType.ALL設置為Book實體,則可以進一步推動該問題:

@ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "Book_Author",joinColumns = {@JoinColumn(name = "book_id", referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "author_id", referencedColumnName = "id")} )

這次,不僅書籍被刪除,而且作者也被刪除:

doInTransaction(session -> {Author _Mark_Armstrong = getByName(session, "Mark Armstrong");session.delete(_Mark_Armstrong);Author _John_Smith = getByName(session, "John Smith");assertNull(_John_Smith); });

作者的刪除觸發所有相關書籍的刪除,這進一步觸發所有相關的作者的刪除。 這是一個非常危險的操作,會導致大規模實體刪除,這很少是預期的行為。

SELECT manytomany0_.id AS id1_0_,manytomany0_.full_name AS full_nam2_0_ FROM author manytomany0_ WHERE manytomany0_.full_name = 'Mark Armstrong' SELECT books0_.author_id AS author_i2_0_0_,books0_.book_id AS book_id1_2_0_,manytomany1_.id AS id1_1_1_,manytomany1_.title AS title2_1_1_ FROM book_author books0_ INNER JOIN book manytomany1_ON books0_.book_id = manytomany1_.id WHERE books0_.author_id = 3 SELECT authors0_.book_id AS book_id1_1_0_,authors0_.author_id AS author_i2_2_0_,manytomany1_.id AS id1_0_1_,manytomany1_.full_name AS full_nam2_0_1_ FROM book_author authors0_ INNER JOIN author manytomany1_ON authors0_.author_id = manytomany1_.id WHERE authors0_.book_id = 2 SELECT books0_.author_id AS author_i2_0_0_,books0_.book_id AS book_id1_2_0_,manytomany1_.id AS id1_1_1_,manytomany1_.title AS title2_1_1_ FROM book_author books0_ INNER JOIN book manytomany1_ON books0_.book_id = manytomany1_.id WHERE books0_.author_id = 1 SELECT authors0_.book_id AS book_id1_1_0_,authors0_.author_id AS author_i2_2_0_,manytomany1_.id AS id1_0_1_,manytomany1_.full_name AS full_nam2_0_1_ FROM book_author authors0_ INNER JOIN author manytomany1_ON authors0_.author_id = manytomany1_.id WHERE authors0_.book_id = 1 SELECT books0_.author_id AS author_i2_0_0_,books0_.book_id AS book_id1_2_0_,manytomany1_.id AS id1_1_1_,manytomany1_.title AS title2_1_1_ FROM book_author books0_ INNER JOIN book manytomany1_ON books0_.book_id = manytomany1_.id WHERE books0_.author_id = 2 delete from Book_Author where book_id=2 delete from Book_Author where book_id=1 delete from Author where id=2 delete from Book where id=1 delete from Author where id=1 delete from Book where id=2 delete from Author where id=3

這種用例在很多方面都是錯誤的。 大量不必要的SELECT語句,最終我們最終刪除了所有作者及其所有書籍。 這就是為什么當您在多對多關聯中發現CascadeType.ALL時,它應該引起您的注意。

當涉及到Hibernate映射時,您應該始終追求簡單性。 Hibernate文檔也證實了這一假設:

真正的多對多關聯的實際測試案例很少見。 大多數時候,您需要存儲在“鏈接表”中的其他信息。 在這種情況下,最好將兩個一對多關聯用于中間鏈接類。 實際上,大多數關聯是一對多和多對一的。 因此,在使用任何其他關聯樣式時,您應謹慎進行。

結論

級聯是一種方便的ORM功能,但并非沒有問題。 您應該僅從父級實體級聯到子級,而不是相反。 您應該始終僅使用業務邏輯要求所要求的Casacde操作,而不應將CascadeType.ALL轉換為默認的Parent-Child關聯實體狀態傳播配置。

  • 代碼可在GitHub上獲得 。

翻譯自: https://www.javacodegeeks.com/2015/03/a-beginners-guide-to-jpa-and-hibernate-cascade-types.html

jpa和hibernate

總結

以上是生活随笔為你收集整理的jpa和hibernate_JPA和Hibernate级联类型的初学者指南的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。