2009-07-04 31 views
65

Giả sử tôi có hai thực thể: Nhóm và Người dùng. Mỗi người dùng có thể là thành viên của nhiều nhóm và mỗi nhóm có thể có nhiều người dùng.Làm thế nào để loại bỏ thực thể với mối quan hệ ManyToMany trong JPA (và tương ứng với các hàng của bảng)?

@Entity 
public class User { 
    @ManyToMany 
    Set<Group> groups; 
    //... 
} 

@Entity 
public class Group { 
    @ManyToMany(mappedBy="groups") 
    Set<User> users; 
    //... 
} 

Bây giờ tôi muốn xóa nhóm (giả sử nó có nhiều thành viên).

Vấn đề là khi tôi gọi EntityManager.remove() trên một số nhà cung cấp Group, JPA (trong trường hợp của tôi Hibernate) không loại bỏ các hàng từ bảng tham gia và xóa các hoạt động không do ép chính nước ngoài. Gọi remove() trên User hoạt động tốt (tôi đoán điều này có liên quan đến việc sở hữu một bên của mối quan hệ).

Vậy làm cách nào để xóa nhóm trong trường hợp này?

Cách duy nhất tôi có thể đưa ra là tải tất cả người dùng trong nhóm, sau đó cho mọi người dùng xóa nhóm hiện tại khỏi nhóm của mình và cập nhật người dùng. Nhưng nó có vẻ vô lý với tôi để gọi update() trên mỗi người dùng từ nhóm chỉ để có thể xóa nhóm này.

Trả lời

55
  • Quyền sở hữu mối quan hệ được xác định bởi nơi bạn đặt thuộc tính 'mappedBy' vào chú thích. Đối tượng bạn đặt 'mappedBy' là đối tượng KHÔNG phải là chủ sở hữu. Không có cơ hội cho cả hai bên là chủ sở hữu. Nếu bạn không có trường hợp sử dụng 'xóa người dùng', bạn có thể chỉ cần chuyển quyền sở hữu cho thực thể Group, vì hiện tại, User là chủ sở hữu.
  • Mặt khác, bạn chưa hỏi về điều đó, nhưng có một điều đáng để biết. groupsusers không được kết hợp với nhau. Ý tôi là, sau khi xóa cá thể User1 khỏi Group1.users, các bộ sưu tập User1.groups không tự động thay đổi (điều này khá ngạc nhiên đối với tôi),
  • Tất cả trong tất cả, tôi sẽ đề nghị bạn quyết định ai là chủ sở hữu. Giả sử rằng User là chủ sở hữu. Sau đó, khi xóa người dùng, nhóm người dùng quan hệ sẽ được cập nhật tự động. Nhưng khi xóa một nhóm bạn phải chăm sóc xóa các mối quan hệ cho mình như thế này:

entityManager.remove(group) 
for (User user : group.users) { 
    user.groups.remove(group); 
} 
... 
// then merge() and flush() 
+0

Thnx! Tôi đã có cùng một vấn đề và giải pháp của bạn giải quyết nó. Nhưng tôi phải biết nếu có một cách khác để giải quyết vấn đề này. Nó tạo ra một mã khủng khiếp. Tại sao nó không thể là _em.remove (thực thể) _ và đó là nó? –

+1

Điều này có được tối ưu hóa đằng sau hậu trường không? Tôi không muốn truy vấn toàn bộ tập dữ liệu. – Ced

+0

Đây là phương pháp thực sự tồi tệ, nếu bạn có hàng ngàn người dùng trong nhóm đó thì sao? – Serg

20

Tôi tìm thấy một giải pháp khả thi, nhưng ... Tôi không biết nếu đó là một giải pháp tốt .

@Entity 
public class Role extends Identifiable { 

    @ManyToMany(cascade ={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) 
    @JoinTable(name="Role_Permission", 
      [email protected](name="Role_id"), 
      [email protected](name="Permission_id") 
     ) 
    public List<Permission> getPermissions() { 
     return permissions; 
    } 

    public void setPermissions(List<Permission> permissions) { 
     this.permissions = permissions; 
    } 
} 

@Entity 
public class Permission extends Identifiable { 

    @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) 
    @JoinTable(name="Role_Permission", 
      [email protected](name="Permission_id"), 
      [email protected](name="Role_id") 
     ) 
    public List<Role> getRoles() { 
     return roles; 
    } 

    public void setRoles(List<Role> roles) { 
     this.roles = roles; 
    } 

Tôi đã thử cách này và nó hoạt động. Khi bạn xóa Role, các mối quan hệ sẽ bị xóa (nhưng không phải là các thực thể Permission) và khi bạn xóa Permission, các mối quan hệ với Role cũng bị xóa (nhưng không phải là cá thể Role). Nhưng chúng tôi đang lập bản đồ một mối quan hệ một chiều hai lần và cả hai thực thể là chủ sở hữu của mối quan hệ. Điều này có thể gây ra một số vấn đề cho Hibernate? Loại vấn đề nào?

Cảm ơn!

Mã ở trên là từ một liên kết khác post.

+0

bộ sưu tập làm mới vấn đề? – jelies

+0

Tại sao câu trả lời này không được bình chọn là câu trả lời được chấp thuận? – kinkajou

+9

Điều này không chính xác. ManyToMany hai chiều nên có một và chỉ một bên chủ sở hữu của mối quan hệ. Giải pháp này là làm cho cả hai bên là chủ sở hữu và cuối cùng sẽ dẫn đến bản ghi trùng lặp. Để tránh các bản ghi trùng lặp, Bộ phải được sử dụng thay cho Danh sách. Tuy nhiên, việc sử dụng Set chỉ là một giải pháp để thực hiện điều gì đó không được khuyến nghị. –

3

Đối với giá trị của nó, tôi đang sử dụng EclipseLink 2.3.2.v20111125-r10461 và nếu tôi có mối quan hệ một chiều @ManyToMany, tôi quan sát vấn đề mà bạn mô tả. Tuy nhiên, nếu tôi thay đổi nó thành mối quan hệ hai chiều @ManyToMany, tôi có thể xóa một thực thể khỏi bên không sở hữu và bảng JOIN được cập nhật một cách thích hợp. Đây là tất cả mà không sử dụng bất kỳ thuộc tính tầng.

31

Các công trình sau đây dành cho tôi.Thêm phương pháp sau đây để các thực thể đó không phải là chủ sở hữu của mối quan hệ (Nhóm)

@PreRemove 
private void removeGroupsFromUsers() { 
    for (User u : users) { 
     u.getGroups().remove(this); 
    } 
} 

Hãy ghi nhớ rằng để làm việc này, Tập đoàn phải có một danh sách cập nhật CỦA NGƯỜI SỬ DỤNG (mà không được thực hiện tự động) . do đó, mỗi khi bạn thêm một Nhóm vào danh sách nhóm trong thực thể Người dùng, bạn cũng nên thêm Người dùng vào danh sách người dùng trong thực thể Nhóm.

+0

cascade = CascadeType.ALL không được đặt khi bạn muốn sử dụng giải pháp này. Nếu không nó hoạt động hoàn hảo! – ltsstar

6

Để thay thế cho giải pháp JPA/Hibernate: bạn có thể sử dụng một CASCADE DELETE khoản trong định nghĩa cơ sở dữ liệu của chính foregin của bạn trên tham gia bảng của bạn, chẳng hạn như (Oracle cú pháp):

CONSTRAINT fk_to_group 
    FOREIGN KEY (group_id) 
    REFERENCES group (id) 
    ON DELETE CASCADE 

Bằng cách đó các Bản thân DBMS tự động xóa hàng trỏ đến nhóm khi bạn xóa nhóm. Và nó hoạt động cho dù xóa được thực hiện từ Hibernate/JPA, JDBC, bằng tay trong DB hoặc bất kỳ cách nào khác.

tính năng xóa tầng được hỗ trợ bởi tất cả các DBMS chính (Oracle, MySQL, SQL Server, PostgreSQL).

1

Đây là giải pháp tốt. Phần tốt nhất là ở phía SQL - tinh chỉnh đến bất kỳ mức độ nào là dễ dàng.

Tôi đã sử dụng MySql và MySql Workbench để Cascade khi xóa cho khóa ngoài yêu cầu.

ALTER TABLE schema.joined_table 
ADD CONSTRAINT UniqueKey 
FOREIGN KEY (key2) 
REFERENCES schema.table1 (id) 
ON DELETE CASCADE; 
+0

Chào mừng bạn đến với SO. Vui lòng dành một chút thời gian và xem xét điều này để cải thiện định dạng của bạn và đọc bằng chứng: https://stackoverflow.com/help/how-to-ask – petezurich

0

này làm việc cho tôi:

@Transactional 
public void remove(Integer groupId) { 
    Group group = groupRepository.findOne(groupId); 
    group.getUsers().removeAll(group.getUsers()); 

    // Other business logic 

    groupRepository.delete(group); 
} 

Ngoài ra, đánh dấu phương pháp @Transactional (org.springframework.transaction.annotation.Transactional), điều này sẽ làm toàn bộ quá trình trong một phiên giao dịch, tiết kiệm thời gian .

Các vấn đề liên quan