JPA和Hibernate级联类型的初学者指南
介紹
JPA將實(shí)體狀態(tài)轉(zhuǎn)換轉(zhuǎn)換為數(shù)據(jù)庫DML語句。 由于對實(shí)體圖進(jìn)行操作很常見,因此JPA允許我們將實(shí)體狀態(tài)更改從父級傳播到子級 。
通過CascadeType映射配置此行為。
JPA與Hibernate級聯(lián)類型
Hibernate支持所有JPA級聯(lián)類型和一些其他舊式級聯(lián)樣式。 下表繪制了JPA級聯(lián)類型與其等效的Hibernate本機(jī)API之間的關(guān)聯(lián):
| 分離(實(shí)體) | 分離 | 逐出(實(shí)體) | 分離或 EVICT | 默認(rèn)驅(qū)逐事件偵聽器 |
| 合并(實(shí)體) | 合并 | 合并(實(shí)體) | 合并 | 默認(rèn)合并事件監(jiān)聽器 |
| 堅(jiān)持(實(shí)體) | 堅(jiān)持 | 堅(jiān)持(實(shí)體) | 堅(jiān)持 | 默認(rèn)的持久事件監(jiān)聽器 |
| 刷新(實(shí)體) | 刷新 | 刷新(實(shí)體) | 刷新 | 默認(rèn)刷新事件監(jiān)聽器 |
| 刪除(實(shí)體) | 去掉 | 刪除(實(shí)體) | 刪除或刪除 | 默認(rèn)刪除事件監(jiān)聽器 |
| saveOrUpdate(實(shí)體) | SAVE_UPDATE | 默認(rèn)的保存或更新事件監(jiān)聽器 | ||
| 復(fù)制(實(shí)體,復(fù)制模式) | 復(fù)制 | 默認(rèn)復(fù)制事件監(jiān)聽器 | ||
| 鎖(實(shí)體,lockModeType) | buildLockRequest(實(shí)體,lockOptions) | 鎖 | 默認(rèn)鎖定事件監(jiān)聽器 | |
| 以上所有EntityManager方法 | 所有 | 以上所有的Hibernate Session方法 | 所有 |
從該表可以得出以下結(jié)論:
- 在JPA EntityManager或Hibernate Session上調(diào)用persist , merge或refresh沒有什么區(qū)別。
- JPA的remove和detach調(diào)用被委托給Hibernate Delete和逐出本機(jī)操作。
- 只有Hibernate支持復(fù)制和saveOrUpdate 。 盡管復(fù)制對于某些非常特定的場景很有用(當(dāng)確切的實(shí)體狀態(tài)需要在兩個(gè)不同的數(shù)據(jù)源之間進(jìn)行鏡像時(shí)),但持久 合并合并始終是比本機(jī)saveOrUpdate操作更好的替代方法。將持久性用于TRANSIENT實(shí)體,將其用于已分離的實(shí)體。saveOrUpdate的缺點(diǎn)(將分離的實(shí)體快照傳遞給已經(jīng)管理該實(shí)體的Session時(shí) )導(dǎo)致了合并操作的前身:現(xiàn)已不存在的saveOrUpdateCopy操作。
- JPA鎖定方法與Hibernate鎖定請求方法具有相同的行為。
- JPA CascadeType.ALL不僅適用于EntityManager狀態(tài)更改操作,而且還適用于所有Hibernate CascadeTypes 。因此,如果將關(guān)聯(lián)與CascadeType.ALL映射,您仍然可以級聯(lián)Hibernate特定事件。 例如,即使JPA沒有定義LOCK CascadeType ,您也可以級聯(lián)JPA鎖定操作(盡管它表現(xiàn)為重新附加,而不是實(shí)際的鎖定請求傳播)。
級聯(lián)最佳做法
級聯(lián)僅對父級 - 子級關(guān)聯(lián)有意義( 父級實(shí)體狀態(tài)轉(zhuǎn)換級聯(lián)到其子級實(shí)體)。 從孩子級聯(lián)到父級不是很有用,通常是映射代碼的味道。
接下來,我將采取分析所有JPA 家長的級聯(lián)行為- 子關(guān)聯(lián)。
一對一
最常見的一對一雙向關(guān)聯(lián)如下所示:
@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實(shí)體扮演Parent角色,而PostDetails是Child 。
雙向關(guān)聯(lián)應(yīng)始終在兩側(cè)進(jìn)行更新,因此父級側(cè)應(yīng)包含addChild和removeChild組合。 這些方法確保我們始終同步關(guān)聯(lián)的雙方,以避免對象或關(guān)系數(shù)據(jù)損壞問題。
在這種特定情況下,刪除CascadeType.ALL和孤立的孤島是有意義的,因?yàn)镻ostDetails生命周期與其后 父實(shí)體的生命周期綁定在一起。
進(jìn)行一對一的持久化操作
CascadeType.PERSIST與CascadeType.ALL配置一起提供,因此我們只需要持久化Post實(shí)體,并且關(guān)聯(lián)的PostDetails實(shí)體也可以持久化:
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)級聯(lián)一對一合并操作
CascadeType.MERGE繼承自CascadeType.ALL設(shè)置,因此我們只需要合并Post實(shí)體,并且關(guān)聯(lián)的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級聯(lián)一對一刪除操作
CascadeType.REMOVE也是從CascadeType.ALL配置繼承的,因此Post實(shí)體刪除也會觸發(fā)PostDetails實(shí)體刪除:
Post post = newPost();doInTransaction(session -> {session.delete(post); });生成以下輸出:
delete from PostDetails where id = 1 delete from Post where id = 1一對一刪除孤立級聯(lián)操作
如果一個(gè)孩子實(shí)體從母公司分離,兒童外鍵設(shè)置為NULL。 如果我們也要?jiǎng)h除“ 子行”,則必須使用孤立刪除支持。
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單向一對一關(guān)聯(lián)
大多數(shù)情況下, 父實(shí)體是反方(如的mappedBy), 兒童 controling通過它的外鍵關(guān)聯(lián)。 但是級聯(lián)不限于雙向關(guān)聯(lián),我們還可以將其用于單向關(guān)系:
@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;} }層疊在于傳播父實(shí)體狀態(tài)過渡到一個(gè)或多個(gè)兒童的實(shí)體,它可用于單向和雙向關(guān)聯(lián)。
一對多
最常見的父 - 子關(guān)聯(lián)由一到多和多到一的關(guān)系,其中級聯(lián)是只對一個(gè)一對多側(cè)有用:
@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和孤立刪除是合適的,因?yàn)镃omment生命周期綁定到其Post Parent實(shí)體的生命周期。
級聯(lián)一對多持久化操作
我們只需要保留Post實(shí)體,所有相關(guān)的Comment實(shí)體也將保留:
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!')級聯(lián)一對多合并操作
合并Post實(shí)體也將合并所有Comment實(shí)體:
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級聯(lián)一對多刪除操作
刪除Post實(shí)體后,關(guān)聯(lián)的Comment實(shí)體也將被刪除:
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一對多刪除孤立級聯(lián)操作
移除孤兒使我們可以在父實(shí)體不再引用子實(shí)體時(shí)將其刪除:
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多對多
多對多關(guān)系是棘手的,因?yàn)榇岁P(guān)聯(lián)的每一方都扮演“ 父母”和“ 孩子”角色。 盡管如此,我們?nèi)钥梢詮奈覀円獋鞑?shí)體狀態(tài)更改的地方識別出一側(cè)。
我們不應(yīng)該默認(rèn)使用CascadeType.ALL ,因?yàn)镃ascadeTpe.REMOVE最終可能會刪除比我們期望的更多的內(nèi)容(您很快就會發(fā)現(xiàn)):
@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;} }級聯(lián)多對多持久操作
堅(jiān)持作者實(shí)體也將保留書籍 :
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)解除多對多關(guān)聯(lián)的一側(cè)
要?jiǎng)h除Author ,我們需要取消關(guān)聯(lián)屬于可移動(dòng)實(shí)體的所有Book_Author關(guān)系:
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多對多關(guān)聯(lián)會生成太多冗余SQL語句,并且經(jīng)常很難調(diào)整它們。 接下來,我將演示多對多CascadeType.REMOVE隱藏的危險(xiǎn)。
多對多CascadeType.REMOVE陷阱
多對多CascadeType.ALL是另一個(gè)代碼異味,我在查看代碼時(shí)經(jīng)常碰到。 所述CascadeType.REMOVE使用CascadeType.ALL時(shí)自動(dòng)繼承,但實(shí)體去除不僅應(yīng)用到鏈接表,但對關(guān)聯(lián)的另一側(cè)為好。
讓我們將Author實(shí)體書籍多對多關(guān)聯(lián)更改為使用CascadeType.ALL代替:
@ManyToMany(mappedBy = "authors", cascade = CascadeType.ALL) private List<Book> books = new ArrayList<>();刪除一位作者時(shí) :
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()); });屬于已刪除作者的所有圖書都將被刪除,即使我們?nèi)耘c已刪除圖書相關(guān)聯(lián)的其他作者也是如此:
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通常,此行為與業(yè)務(wù)邏輯期望不符,僅在首次刪除實(shí)體時(shí)才發(fā)現(xiàn)。
如果我們也將CascadeType.ALL設(shè)置為Book實(shí)體,則可以進(jìn)一步推動(dòng)該問題:
@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); });作者的刪除觸發(fā)所有相關(guān)書籍的刪除,這進(jìn)一步觸發(fā)所有相關(guān)的作者的刪除。 這是一個(gè)非常危險(xiǎn)的操作,會導(dǎo)致大規(guī)模實(shí)體刪除,這很少是預(yù)期的行為。
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這種用例在很多方面都是錯(cuò)誤的。 大量不必要的SELECT語句,最終我們最終刪除了所有作者及其所有書籍。 這就是為什么當(dāng)您在多對多關(guān)聯(lián)中發(fā)現(xiàn)CascadeType.ALL時(shí),它應(yīng)該引起您的注意。
當(dāng)涉及到Hibernate映射時(shí),您應(yīng)該始終追求簡單性。 Hibernate文檔也證實(shí)了這一假設(shè):
真正的多對多關(guān)聯(lián)的實(shí)際測試案例很少見。 大多數(shù)時(shí)候,您需要存儲在“鏈接表”中的其他信息。 在這種情況下,最好將兩個(gè)一對多關(guān)聯(lián)用于中間鏈接類。 實(shí)際上,大多數(shù)關(guān)聯(lián)是一對多和多對一的。 因此,在使用任何其他關(guān)聯(lián)樣式時(shí),您應(yīng)謹(jǐn)慎進(jìn)行。
結(jié)論
級聯(lián)是一種方便的ORM功能,但并非沒有問題。 您應(yīng)該僅從父級實(shí)體級聯(lián)到子級,而不是相反。 您應(yīng)該始終僅使用業(yè)務(wù)邏輯要求所要求的Casacde操作,而不應(yīng)將CascadeType.ALL轉(zhuǎn)換為默認(rèn)的Parent-Child關(guān)聯(lián)實(shí)體狀態(tài)傳播配置。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/03/a-beginners-guide-to-jpa-and-hibernate-cascade-types.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的JPA和Hibernate级联类型的初学者指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 避免在ConcurrentHashMap
- 下一篇: 在Graphite中存储Hystrix的