JPA: xóa nhiều đối một và nhiều tầng một chiều


95

Giả sử tôi có mối quan hệ một chiều @ManyToOne như sau:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;
}

@Entity
public class Child implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne
    @JoinColumn
    private Parent parent;  
}

Nếu tôi có cha mẹ P và con cái C 1 ... C n tham chiếu trở lại P, có cách nào tốt và hay trong JPA để tự động loại bỏ các con C 1 ... C n khi P bị loại bỏ (tức là entityManager.remove(P)) không?

Những gì tôi đang tìm kiếm là một chức năng tương tự như ON DELETE CASCADEtrong SQL.


1
Ngay cả khi chỉ 'Con' có tham chiếu đến 'Gốc' (theo cách đó, tham chiếu là một chiều) thì việc bạn thêm Danh sách 'Con' với ánh xạ '@OneToMany' và thuộc tính 'Cascade = ALL' vào trong 'Cha mẹ'? Tôi giả định rằng JPA nên giải quyết rằng thậm chí khó khăn chỉ có 1 bên giữ tham chiếu.
kvDennis

1
@kvDennis, có những trường hợp bạn không muốn kết hợp chặt chẽ nhiều bên với một bên. Ví dụ như trong ACL giống như các thiết lập nơi điều khoản bảo mật trong suốt "add-on"
Bachi

Câu trả lời:


73

Các mối quan hệ trong JPA luôn là một chiều, trừ khi bạn liên kết cha mẹ với con theo cả hai hướng. Xếp tầng các phép toán REMOVE từ cha đến con sẽ yêu cầu mối quan hệ từ cha đến con (không chỉ ngược lại).

Do đó, bạn sẽ cần phải làm điều này:

  • Hoặc, thay đổi @ManyToOnemối quan hệ một chiều thành mối quan hệ hai chiều @ManyToOnehoặc một chiều @OneToMany. Sau đó, bạn có thể phân tầng các thao tác XÓA để EntityManager.removeloại bỏ cha mẹ và con cái. Bạn cũng có thể chỉ định orphanRemovallà true, để xóa bất kỳ trẻ em mồ côi nào khi thực thể con trong tập hợp mẹ được đặt thành null, tức là xóa con khi nó không có trong bất kỳ tập hợp mẹ nào.
  • Hoặc, chỉ định ràng buộc khóa ngoại trong bảng con là ON DELETE CASCADE. Bạn sẽ cần gọi EntityManager.clear()sau khi gọi EntityManager.remove(parent)vì ngữ cảnh liên tục cần được làm mới - các thực thể con không được tồn tại trong ngữ cảnh tồn tại sau khi chúng đã bị xóa trong cơ sở dữ liệu.

7
có cách nào để làm No2 với chú thích JPA không?
user2573153

3
Làm cách nào để thực hiện No2 với ánh xạ xml Hibernate?
arg20

92

Nếu bạn đang sử dụng chế độ ngủ đông làm nhà cung cấp JPA, bạn có thể sử dụng chú thích @OnDelete. Chú thích này sẽ thêm vào mối quan hệ trình kích hoạt ON DELETE CASCADE , ủy quyền việc xóa con vào cơ sở dữ liệu.

Thí dụ:

public class Parent {

        @Id
        private long id;

}


public class Child {

        @Id
        private long id;

        @ManyToOne
        @OnDelete(action = OnDeleteAction.CASCADE)
        private Parent parent;
}

Với giải pháp này, mối quan hệ một chiều từ trẻ đến cha mẹ là đủ để tự động loại bỏ tất cả trẻ em. Giải pháp này không cần bất kỳ người nghe nào, v.v. Ngoài ra, một truy vấn như DELETE FROM Parent WHERE id = 1 sẽ loại bỏ các phần tử con.


4
Tôi không thể làm cho nó hoạt động theo cách này, có phiên bản cụ thể nào của hibernate hoặc ví dụ khác chi tiết hơn như thế này không?
Mardari

3
Thật khó để nói tại sao nó không hoạt động với bạn. Để làm cho điều này hoạt động, bạn có thể cần phải tạo lại lược đồ hoặc bạn phải thêm xóa tầng theo cách thủ công. Chú thích @OnDelete dường như đã tồn tại trong một thời gian vì vậy tôi sẽ không đoán rằng phiên bản là một vấn đề.
Thomas Hunziker

10
Cảm ơn vì câu trả lời. Lưu ý nhanh: trình kích hoạt tầng cơ sở dữ liệu sẽ chỉ được tạo nếu bạn đã bật tạo DDL qua chế độ ngủ đông. Nếu không, bạn sẽ phải thêm nó theo cách khác (ví dụ: liquibase) để cho phép các truy vấn đặc biệt chạy trực tiếp trên DB như 'DELETE FROM Parent WHERE id = 1' thực hiện loại bỏ theo tầng.
mjj1409

1
Điều này không hoạt động khi hiệp hội là @OneToOneBất kỳ ý tưởng làm thế nào để giải quyết nó với @OneToOne?
stakowerflol 21/12/18

1
@ThomasHunziker điều này sẽ không hoạt động cho orphanRemoval phải không?
oxyt

13

Tạo mối quan hệ hai chiều, như sau:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private Set<Child> children;
}

8
câu trả lời xấu, mối quan hệ hai chiều là khủng khiếp trong JPA vì hoạt động đối với trẻ em bộ lớn có số lượng đáng kinh ngạc của thời gian
Enerccio

1
Có bằng chứng nào cho thấy mối quan hệ hai chiều là chậm không?
shalama

@enerccio Điều gì sẽ xảy ra nếu mối quan hệ hai chiều là một-một? Ngoài ra, xin vui lòng hiển thị một bài báo nói rằng mối quan hệ hai chiều là chậm? chậm trong cái gì? truy xuất? xóa? đang cập nhật?
saran3h

@ saran3h mọi thao tác (thêm, bớt) sẽ tải tất cả các phần tử con, do đó, tải dữ liệu khổng lồ có thể vô dụng (chẳng hạn như việc thêm một giá trị không yêu cầu tải tất cả các phần tử con từ cơ sở dữ liệu, đó chính là điều mà ánh xạ này thực hiện).
Enerccio

@Enerccio Tôi nghĩ rằng tất cả mọi người đều sử dụng tính năng tải chậm khi tham gia. Vì vậy, làm thế nào nó vẫn là một vấn đề hiệu suất?
saran3h

1

Tôi đã thấy trong @ManytoOne một chiều, xóa không hoạt động như mong đợi. Khi cha mẹ bị xóa, lý tưởng nhất là con cũng nên bị xóa, nhưng chỉ cha mẹ bị xóa và con KHÔNG bị xóa và bị bỏ lại là trẻ mồ côi

Công nghệ được sử dụng là Spring Boot / Spring Data JPA / Hibernate

Khởi động Sprint: 2.1.2.RELEASE

Dữ liệu mùa xuân JPA / Hibernate được sử dụng để xóa hàng .eg

parentRepository.delete(parent)

ParentRepository mở rộng kho lưu trữ CRUD tiêu chuẩn như hình dưới đây ParentRepository extends CrudRepository<T, ID>

Sau đây là lớp thực thể của tôi

@Entity(name = child”)
public class Child  {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne( fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = parent_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Parent parent;
}

@Entity(name = parent”)
public class Parent {

    @Id
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 50)
    private String firstName;


}

Tôi đã tìm thấy giải pháp cho lý do tại sao xóa không hoạt động. rõ ràng là hibernate KHÔNG sử dụng mysql Engine -INNODB, bạn cần engine INNODB cho mysql để tạo ràng buộc khóa ngoại. Sử dụng các thuộc tính sau trong application.properties, khởi động mùa xuân / ngủ đông để sử dụng công cụ mysql INNODB. Vì vậy, ràng buộc khóa ngoại hoạt động và do đó cũng xóa thác
ranjesh

Thuộc tính bị bỏ lỡ sử dụng trong bình luận trước đó. Sau đây là những tính chất mùa xuân sử dụngspring.jpa.hibernate.use-new-id-generator-mappings=true spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
ranjesh

FYI, bạn đã sai "trong mã. Xemname= "parent"
alexander

0

Sử dụng cách này để chỉ xóa một mặt

    @ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
//  @JoinColumn(name = "qid")
    @JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = @ForeignKey(name = "qid"), nullable = false)
    // @JsonIgnore
    @JsonBackReference
    private QueueGroup queueGroup;

-1

@Cascade (org.hibernate.annotations.CascadeType.DELETE_ORPHAN)

Chú thích đã đưa ra làm việc cho tôi. Có thể thử

Ví dụ :-

     public class Parent{
            @Id
            @GeneratedValue(strategy=GenerationType.AUTO)
            @Column(name="cct_id")
            private Integer cct_id;
            @OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER,mappedBy="clinicalCareTeam", orphanRemoval=true)
            @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
            private List<Child> childs;
        }
            public class Child{
            @ManyToOne(fetch=FetchType.EAGER)
            @JoinColumn(name="cct_id")
            private Parent parent;
    }
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.