JPA orphanRemoval = true khác với mệnh đề ON DEL DEL CASCADE DML như thế nào


184

Tôi hơi bối rối về orphanRemovalthuộc tính JPA 2.0 .

Tôi nghĩ rằng tôi có thể thấy nó là cần thiết khi tôi sử dụng các công cụ tạo DB của nhà cung cấp JPA của tôi để tạo cơ sở dữ liệu DDL cơ bản để có ON DELETE CASCADEmối quan hệ cụ thể.

Tuy nhiên, nếu DB tồn tại và nó đã có một ON DELETE CASCADEmối quan hệ, điều này không đủ để xếp tầng xóa một cách thích hợp? Không những gì orphanRemovallàm ngoài?

Chúc mừng

Câu trả lời:


291

orphanRemovalkhông có gì để làm với ON DELETE CASCADE.

orphanRemovallà một điều hoàn toàn cụ thể ORM . Nó đánh dấu thực thể "con" sẽ bị xóa khi nó không còn được tham chiếu từ thực thể "cha mẹ", ví dụ như khi bạn xóa thực thể con khỏi bộ sưu tập tương ứng của thực thể cha.

ON DELETE CASCADElà một thứ dành riêng cho cơ sở dữ liệu , nó sẽ xóa hàng "con" trong cơ sở dữ liệu khi hàng "cha" bị xóa.


3
Điều này có nghĩa là chúng có hiệu quả an toàn, nhưng một hệ thống khác chịu trách nhiệm cho việc đó xảy ra?
Anonymousoose

101
Anon, nó không có tác dụng tương tự. TRÊN XÓA CASCADE yêu cầu DB xóa tất cả các bản ghi con khi cha mẹ bị xóa. Đó là nếu tôi xóa HÓA ĐƠN, sau đó xóa tất cả các MỤC trên HÓA ĐƠN đó. OrphanRemoval nói với ORM rằng nếu tôi xóa một đối tượng Item khỏi bộ sưu tập Vật phẩm thuộc về một đối tượng Hóa đơn (trong hoạt động bộ nhớ), sau đó "lưu" Hóa đơn, thì vật phẩm bị xóa sẽ bị xóa khỏi DB bên dưới.
garyKeorkunian

2
Nếu bạn sử dụng mối quan hệ đơn phương, thì đứa trẻ mồ côi sẽ tự động bị xóa ngay cả khi bạn không đặt orphanRemoval = true
Tim

98

Một ví dụ được hình thành ở đây :

Khi một Employeeđối tượng thực thể bị loại bỏ, hoạt động gỡ bỏ được xếp tầng vào Addressđối tượng thực thể được tham chiếu . Về vấn đề này, orphanRemoval=truecascade=CascadeType.REMOVElà giống hệt nhau, và nếu orphanRemoval=trueđược chỉ định, CascadeType.REMOVElà dư thừa.

Sự khác biệt giữa hai cài đặt là trong phản ứng ngắt kết nối mối quan hệ. Ví dụ, chẳng hạn như khi đặt trường địa chỉ thành nullhoặc cho Addressđối tượng khác .

  • Nếu orphanRemoval=trueđược chỉ định, phiên bản ngắt kết nối Addresssẽ tự động bị xóa. Điều này hữu ích để làm sạch các đối tượng phụ thuộc (ví dụ Address) không nên tồn tại mà không có tham chiếu từ đối tượng chủ sở hữu (ví dụ Employee).

  • Nếu chỉ cascade=CascadeType.REMOVEđược chỉ định, không có hành động tự động nào được thực hiện do ngắt kết nối mối quan hệ không phải là thao tác xóa.

Để tránh treo tài liệu tham khảo do loại bỏ mồ côi, tính năng này chỉ nên được bật cho các trường chứa các đối tượng phụ thuộc không chia sẻ riêng tư.

Tôi hy vọng điều này làm cho nó rõ ràng hơn.


Sau khi đọc câu trả lời của bạn, tôi nhận ra sự khác biệt chính xác giữa cả hai và vấn đề của tôi đã được giải quyết. Tôi đã bị mắc kẹt trong việc xóa các thực thể con khỏi cơ sở dữ liệu, nếu chúng bị ngắt kết nối (loại bỏ) khỏi bộ sưu tập được xác định trong thực thể cha. Tôi cũng đã hỏi câu hỏi ' stackoverflow.com/questions/15526440/ trên '. Chỉ cần thêm nhận xét của tôi để liên kết cả hai câu hỏi.
Narendra Verma

@forhas vui lòng xem qua stackoverflow.com/questions/58185249/
Kẻ

46

Khoảnh khắc bạn xóa một thực thể con khỏi bộ sưu tập, bạn cũng sẽ xóa thực thể con đó khỏi DB. orphanRemoval cũng ngụ ý rằng bạn không thể thay đổi cha mẹ; nếu có một bộ phận có nhân viên, một khi bạn loại bỏ nhân viên đó để đưa bộ phận đó vào một bộ phận khác, bạn sẽ vô tình loại bỏ nhân viên đó khỏi DB khi xả / cam kết (trước hết). Tinh thần là đặt mồ côiRemoval thành đúng miễn là bạn chắc chắn rằng con cái của cha mẹ đó sẽ không di cư sang cha mẹ khác trong suốt sự tồn tại của chúng. Bật orphanRemoval cũng tự động thêm XÓA vào danh sách xếp tầng.


3
Chính xác ... cũng được gọi là mối quan hệ cha mẹ / con cái "riêng tư".
HDave

Điều đó có nghĩa là ngay sau khi tôi gọi department.remove(emp);nhân viên đó sẽ bị xóa khỏi bảng emp mà không cần gọicommit()
JavaTechnical

18

Ánh xạ JPA tương đương cho DDL ON DELETE CASCADEcascade=CascadeType.REMOVE. Loại bỏ mồ côi có nghĩa là các thực thể phụ thuộc bị loại bỏ khi mối quan hệ với thực thể "cha mẹ" của chúng bị phá hủy. Ví dụ: nếu một đứa trẻ bị xóa khỏi một @OneToManymối quan hệ mà không loại bỏ nó một cách rõ ràng trong trình quản lý thực thể.


1
cascade=CascadeType.REMOVEKHÔNG tương đương với ON DELETE CASCADE. Bật xóa mã ứng dụng và không ảnh hưởng đến DDL, khác được thực thi trong DB. Xem stackoverflow.com/a/19696859/548473
Grigory Kislin

9

Sự khác biệt là:
- orphanRemoval = true: Thực thể "con" bị xóa khi không còn tham chiếu (cha mẹ của nó có thể không bị xóa).
- CascadeType.REMOVE: Thực thể "Con" chỉ bị xóa khi "Cha mẹ" của nó bị xóa.


6

Vì đây là một câu hỏi rất phổ biến, tôi đã viết bài viết này , trên đó câu trả lời này dựa trên.

Thực thể chuyển trạng thái

JPA dịch các chuyển đổi trạng thái thực thể sang các câu lệnh SQL, như INSERT, UPDATE hoặc DELETE.

Chuyển trạng thái thực thể JPA

Khi bạn persistlà một thực thể, bạn đang lên lịch cho câu lệnh INSERT được thực thi khi được xóa EntityManager, tự động hoặc thủ công.

Khi bạn removelà một thực thể, bạn đang lên lịch cho câu lệnh DELETE, nó sẽ được thực thi khi Bối cảnh liên tục bị xóa.

Chuyển trạng thái thực thể xếp chồng

Để thuận tiện, JPA cho phép bạn tuyên truyền chuyển trạng thái thực thể từ thực thể cha sang con.

Vì vậy, nếu bạn có một Postthực thể cha mẹ có @OneToManyliên kết với PostCommentthực thể con:

Các thực thể Post và PostVer

Bộ commentssưu tập trong Postthực thể được ánh xạ như sau:

@OneToMany(
    mappedBy = "post", 
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<Comment> comments = new ArrayList<>();

CascadeType.ALL

Các cascadethuộc tính nói với các nhà cung cấp JPA để vượt qua sự chuyển đổi trạng thái thực thể từ cha mẹ Posttổ chức cho tất cả PostCommentcác đối tượng chứa trong commentsbộ sưu tập.

Vì vậy, nếu bạn loại bỏ Postthực thể:

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

entityManager.remove(post);

Nhà cung cấp JPA sẽ xóa PostCommentthực thể trước và khi tất cả các thực thể con bị xóa, nó cũng sẽ xóa Postthực thể đó:

DELETE FROM post_comment WHERE id = 1
DELETE FROM post_comment WHERE id = 2

DELETE FROM post WHERE id = 1

Loại bỏ mồ côi

Khi bạn đặt orphanRemovalthuộc tính thành true, nhà cung cấp JPA sẽ lên lịch removehoạt động khi thực thể con bị xóa khỏi bộ sưu tập.

Vì vậy, trong trường hợp của chúng tôi,

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

PostComment postComment = post.getComments().get(0);
assertEquals(1L, postComment.getId());

post.getComments().remove(postComment);

Nhà cung cấp JPA sẽ xóa post_commentbản ghi liên quan do PostCommentthực thể không còn được tham chiếu trong commentsbộ sưu tập:

DELETE FROM post_comment WHERE id = 1

TRÊN CASCADE XÓA

Các ON DELETE CASCADEđược xác định ở cấp FK:

ALTER TABLE post_comment 
ADD CONSTRAINT fk_post_comment_post_id 
FOREIGN KEY (post_id) REFERENCES post 
ON DELETE CASCADE;

Khi bạn làm điều đó, nếu bạn xóa một posthàng:

DELETE FROM post WHERE id = 1

Tất cả các post_commentthực thể liên quan được loại bỏ tự động bởi công cụ cơ sở dữ liệu. Tuy nhiên, đây có thể là một hoạt động rất nguy hiểm nếu bạn xóa nhầm một thực thể gốc.

Phần kết luận

Ưu điểm của JPA cascadeorphanRemovalcác tùy chọn là bạn cũng có thể hưởng lợi từ việc khóa lạc quan để ngăn chặn các cập nhật bị mất .

Nếu bạn sử dụng cơ chế xếp tầng JPA, bạn không cần sử dụng cấp độ DDL ON DELETE CASCADE, đây có thể là một hoạt động rất nguy hiểm nếu bạn xóa một thực thể gốc có nhiều thực thể con ở nhiều cấp độ.


Vì vậy, trong Orphan Removal một phần câu trả lời của bạn: post.getComments (). Remove (postVer); sẽ hoạt động trong ánh xạ hai chiều OneToMany chỉ vì tầng liên tục. Nếu không xếp tầng và không xóa ở phía ManyToOne, như trong ví dụ của bạn, việc xóa kết nối giữa 2 thực thể sẽ không được duy trì trong DB?
aurelije

Loại bỏ mồ côi không bị ảnh hưởng bởi CascadeType. Đó là một cơ chế bổ sung. Bây giờ, bạn đang nhầm lẫn loại bỏ với kiên trì. Loại bỏ mồ côi là về việc xóa các hiệp hội không được ước tính trong khi vẫn tồn tại là về việc cứu các thực thể mới. Bạn cần theo các liên kết được cung cấp trong câu trả lời để hiểu rõ hơn về các khái niệm này.
Vlad Mihalcea

Tôi không hiểu một điều: làm thế nào để trẻ mồ côi loại bỏ việc lập bản đồ hai chiều nếu chúng ta không bao giờ xóa kết nối ở bên M? Tôi nghĩ rằng việc xóa PostVer khỏi danh sách của Post mà không đặt PostVer.post thành null sẽ không dẫn đến việc xóa kết nối giữa 2 thực thể đó trong DB. Đó là lý do tại sao tôi nghĩ rằng việc loại bỏ trẻ mồ côi sẽ không xảy ra, trong thế giới quan hệ có PostVer không phải là trẻ mồ côi. Tôi sẽ kiểm tra nó khi tôi có thời gian rảnh.
aurelije

1
Tôi đã thêm hai ví dụ này vào kho lưu trữ GitHub Java Persistence hiệu suất cao của tôi để trình bày cách tất cả hoạt động. Bạn không cần phải đồng bộ hóa phía con vì bạn thường phải thực hiện để xóa trực tiếp các thực thể. Tuy nhiên, loại bỏ mồ côi chỉ hoạt động nếu thêm tầng, nhưng đó dường như là một giới hạn Hibernate, không phải là thông số kỹ thuật của JPA.
Vlad Mihalcea

5

@GaryK câu trả lời là hoàn toàn tuyệt vời, tôi đã dành một tiếng đồng hồ tìm kiếm một lời giải thích orphanRemoval = truevs CascadeType.REMOVEvà nó đã giúp tôi hiểu được.

Tóm tắt: orphanRemoval = truehoạt động giống hệt như CascadeType.REMOVE CHỈ NẾU chúng ta xóa đối tượng ( entityManager.delete(object)) và chúng ta cũng muốn xóa các đối tượng con .

Trong trường hợp hoàn toàn khác nhau, khi chúng tôi tìm nạp một số dữ liệu như List<Child> childs = object.getChilds()sau đó xóa một đứa trẻ ( entityManager.remove(childs.get(0)) bằng cách sử dụng orphanRemoval=truesẽ khiến thực thể đó tương ứng childs.get(0)sẽ bị xóa khỏi cơ sở dữ liệu.


1
Bạn có một lỗi đánh máy trong đoạn thứ hai của bạn: Không có phương thức nào như entityManager.delete (obj); đó là entityManager.remove (obj).
JL_SO

3

Việc loại bỏ mồ côi có tác dụng tương tự như TRÊN XÓA CASCADE trong trường hợp sau: - Hãy nói rằng chúng ta có một mối quan hệ đơn giản giữa một thực thể sinh viên và một thực thể hướng dẫn, trong đó nhiều sinh viên có thể được ánh xạ tới cùng một hướng dẫn và trong cơ sở dữ liệu, chúng ta có một quan hệ khóa ngoại giữa bảng Sinh viên và Hướng dẫn sao cho bảng sinh viên có id_guide là FK.

    @Entity
    @Table(name = "student", catalog = "helloworld")
    public class Student implements java.io.Serializable {
     @Id
     @GeneratedValue(strategy = IDENTITY)
     @Column(name = "id")
     private Integer id;

    @ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @JoinColumn(name = "id_guide")
    private Guide guide;

// Thực thể mẹ

    @Entity
    @Table(name = "guide", catalog = "helloworld")
    public class Guide implements java.io.Serializable {

/**
 * 
 */
private static final long serialVersionUID = 9017118664546491038L;

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;

@Column(name = "name", length = 45)
private String name;

@Column(name = "salary", length = 45)
private String salary;


 @OneToMany(mappedBy = "guide", orphanRemoval=true) 
 private Set<Student> students = new  HashSet<Student>(0);

Trong kịch bản này, mối quan hệ sao cho thực thể sinh viên là chủ sở hữu của mối quan hệ và do đó chúng ta cần lưu thực thể sinh viên để duy trì toàn bộ biểu đồ đối tượng, ví dụ:

    Guide guide = new Guide("John", "$1500");
    Student s1 = new Student(guide, "Roy","ECE");
    Student s2 = new Student(guide, "Nick", "ECE");
    em.persist(s1);
    em.persist(s2);

Ở đây chúng tôi đang ánh xạ cùng một hướng dẫn với hai đối tượng sinh viên khác nhau và vì CASCADE.PERSIST được sử dụng, biểu đồ đối tượng sẽ được lưu như bên dưới trong bảng cơ sở dữ liệu (trong trường hợp của tôi)

Bảng SINH VIÊN: -

Tên ID Phòng Id_Guide

1 Roy ECE 1

2 Nick ECE 1

Bảng HƯỚNG DẪN: -

ID TÊN Mức lương

1 John $ 1500

và Bây giờ nếu tôi muốn loại bỏ một trong các sinh viên, sử dụng

      Student student1 = em.find(Student.class,1);
      em.remove(student1);

và khi một bản ghi sinh viên bị xóa, bản ghi hướng dẫn tương ứng cũng sẽ bị xóa, đó là nơi thuộc tính CASCADE.REMOVE trong thực thể Sinh viên xuất hiện và nó là gì; nó loại bỏ sinh viên với mã định danh 1 cũng như đối tượng hướng dẫn tương ứng (định danh 1). Nhưng trong ví dụ này, có thêm một đối tượng sinh viên được ánh xạ tới cùng một bản ghi hướng dẫn và trừ khi chúng ta sử dụng thuộc tính orphanRemoval = true trong Thực thể hướng dẫn, mã loại bỏ ở trên sẽ không hoạt động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.