2010-10-21 42 views
15

Tôi biết rằng xóa các đối tượng trẻ mồ côi là một câu hỏi phổ biến đối với người mới đến Hibernate và câu trả lời khá chuẩn là đảm bảo rằng bạn có một số biến thể của cascade=all,delete-orphan hoặc cascade=all-delete-orphan về bộ sưu tập trẻ em.Hibernate có thể xóa các bộ sưu tập mồ côi khi cập nhật một đối tượng tách rời không?

Tôi muốn có khả năng phát hiện Hibernate rằng bộ sưu tập con đã được dọn sạch/xóa khỏi đối tượng cha và có các hàng trong bảng con bị xóa khỏi cơ sở dữ liệu khi đối tượng mẹ được cập nhật. Ví dụ:

Parent parent = session.get(...); 
parent.getChildren().clear(); 
session.update(parent); 

lập bản đồ hiện tại của tôi cho lớp Parent trông giống như:

<bag name="children" cascade="all-delete-orphan"> 
    <key column="parent_id" foreign-key="fk_parent_id"/> 
    <one-to-many class="Child"/> 
</bag> 

này hoạt động tốt đối với tôi khi cập nhật một đối tượng kèm theo, nhưng tôi có một trường hợp sử dụng, trong đó chúng tôi muốn để có thể lấy một đối tượng tách rời (đã được gửi tới phương thức API của chúng ta bởi một máy khách từ xa qua HTTP/JSON) và truyền nó trực tiếp đến Hibernate Session - để cho phép các máy khách có thể thao tác đối tượng cha theo cách nào đó họ thích và có những thay đổi liên tục.

Khi gọi session.update(parent) trên đối tượng tách rời của tôi, các hàng trong bảng con bị mồ côi (cột FK được đặt thành rỗng) nhưng không bị xóa. Lưu ý rằng khi tôi gọi session.update(), đây là lần đầu tiên phiên Hibernate đang xem trường hợp đối tượng này - Tôi không gắn lại hoặc hợp nhất đối tượng với Phiên theo bất kỳ cách nào khác. Tôi dựa vào máy khách để truyền các đối tượng có mã định danh tương ứng với các đối tượng thực trong cơ sở dữ liệu. Ví dụ, logic trong phương pháp dịch vụ API của tôi là một cái gì đó như thế này:

String jsonString = request.getParameter(...); 
Parent parent = deserialize(jsonString); 
session.update(parent); 

Có thể cho Hibernate để phát hiện các bộ sưu tập trẻ em mồ côi trong các đối tượng cha mẹ tách ra khi truyền cho session.update(parent)? Hay tôi đang sử dụng sai đối tượng tách rời theo một cách nào đó?

Hy vọng của tôi là tôi có thể tránh bất kỳ loại tương tác phức tạp nào với Hibernate để duy trì các thay đổi đối với một cá thể tách rời. Phương thức API của tôi không cần phải sửa đổi thêm đối tượng tách rời sau khi gọi tới session.update(parent), phương pháp này chỉ chịu trách nhiệm cho sự thay đổi bền bỉ do các ứng dụng khách từ xa tạo ra.

Trả lời

1

tôi nghĩ, khi sử dụng phiên tách ra, bạn có thể gặp sự cố với bộ sưu tập. tôi sẽ đề nghị bạn đầu tiên tải thực thể với bộ sưu tập, và sau đó cập nhật thực thể đó với các thay đổi, điều đó sẽ giúp ích.

+0

Bạn có nghĩa là, nạp thực hiện và sau đó 'merge () 'nó với cá thể tách rời được truyền cho tôi API? –

+0

@ mattb: bạn có thể viết logic để hợp nhất bộ sưu tập hoặc thậm chí chỉ cần thay bộ sưu tập trước đó và đặt bộ sưu tập mới, nhưng đảm bảo rằng bạn đã thêm thẻ vào hbm của mình. này sẽ chứa truy vấn xóa sql đơn giản để xóa bộ sưu tập hoàn chỉnh trước khi lưu bộ sưu tập mới. hoặc theo những cách khác, bất cứ ai bạn sẽ thêm bất cứ điều gì, xóa sql này sẽ được kích hoạt mọi lúc và sau đó bản sao mới của bộ sưu tập của bạn sẽ được thêm vào. Điều này có thể giải quyết vấn đề của bạn mỗi khi cập nhật bộ sưu tập theo cách thủ công. –

+0

Không chắc chắn nếu tôi thích giải pháp này, vì nó sẽ yêu cầu thay đổi mẫu sử dụng của tôi - thêm '' ví dụ. Đã hy vọng điều này có thể được cố định với bản đồ một mình. –

8

lập bản đồ của bạn (giản thể)

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping package="br.com._3988215.model.domain"> 
    <class name="Parent" table="PARENT"> 
     <id name="id"> 
      <generator class="native"/> 
     </id> 
     <bag cascade="all,delete-orphan" name="childList"> 
      <key column="PARENT_ID" not-null="false"/> 
      <one-to-many class="Child"/> 
     </bag> 
    </class> 
    <class name="Child" table="CHILD"> 
     <id name="id" column="CHILD_ID"> 
      <generator class="native"/> 
     </id> 
    </class> 
</hibernate-mapping> 

sản xuất

PARENT 
    ID 

CHILD 
    CHILD_ID 
    PARENT_ID 

Theo những gì bạn nói

Tôi muốn để có thể có Hibernate phát hiện rằng bộ sưu tập con đã bị xóa từ đối tượng phụ huynh, và có các hàng trong bảng con xóa từ cơ sở dữ liệu khi đối tượng cha mẹ được cập nhật

Something như

Parent parent = session.get(...); 
parent.getChildren().clear(); 

session.update(parent); 

Bạn nói nó hoạt động tốt bởi vì bạn có một cá thể cha mẹ đính kèm

Bây giờ hãy xem phần sau đây (Thông báo Assert.assertNull (thứ hai))

public class WhatYouWantTest { 

    private static SessionFactory sessionFactory; 

    private Serializable parentId; 

    private Serializable firstId; 
    private Serializable secondId; 

    @BeforeClass 
    public static void setUpClass() { 
     Configuration c = new Configuration(); 
     c.addResource("mapping.hbm.3988215.xml"); 

     sessionFactory = c.configure().buildSessionFactory(); 
    } 

    @Before 
    public void setUp() throws Exception { 
     Parent parent = new Parent(); 
     Child first = new Child(); 
     Child second = new Child(); 

     Session session = sessionFactory.openSession(); 
     session.beginTransaction(); 

     parentId = session.save(parent); 
     firstId = session.save(first); 
     secondId = session.save(second); 

     parent.getChildList().add(first); 
     parent.getChildList().add(second); 

     session.getTransaction().commit(); 
     session.close(); 
    } 

    @Test 
    public void removed_second_from_parent_remove_second_from_database() { 
     Parent parent = new Parent(); 
     parent.setId((Integer) parentId); 

     Child first = new Child(); 
     first.setId((Integer) firstId); 

     /** 
      * It simulates the second one has been removed 
      */ 
     parent.getChildList().add(first); 

     Session session = sessionFactory.openSession(); 
     session.beginTransaction(); 

     session.update(parent); 

     session.getTransaction().commit(); 
     session.close(); 

     session = sessionFactory.openSession(); 
     session.beginTransaction(); 

     Child second = (Child) session.get(Child.class, secondId); 
     Assert.assertNull(second); 

     session.getTransaction().commit(); 
     session.close(); 
    } 
} 

Thật không may, kiểm tra không vượt qua. Bạn có thể làm gì ???

  • Kích hoạt qua một cuộc nói chuyện dài chạy

Hibernate tài liệu tham khảo cho biết

mở rộng (hoặc dài) Session - Các Hibernate phiên có thể bị ngắt kết nối từ các kết nối JDBC cơ bản sau khi giao dịch cơ sở dữ liệu có đã được cam kết và kết nối lại khi có yêu cầu của ứng dụng khách mới. Mẫu này được gọi là phiên cho mỗi cuộc hội thoại và thậm chí làm cho việc gắn lại không cần thiết. Phiên bản tự động được sử dụng để cô lập các sửa đổi đồng thời và Phiên thường không được phép tự động xóa, nhưng rõ ràng.

tuyên bố từ chối trách nhiệm: tôi không có bất kỳ tình huống nào sử dụng cuộc trò chuyện dài. Các hạt phiên Java Statefulful hỗ trợ cuộc hội thoại dài. Nhưng hỗ trợ của nó là dành cho JPA (không phải Hibernate)

Hoặc bạn có thể tạo ánh xạ thay thế cho phép Con bạn làm thành phần tổng hợp.Bởi vì vòng đời của nó phụ thuộc vào đối tượng phụ huynh, bạn có thể dựa trên các yếu tố tổng hợp để có được những gì bạn muốn

Tạo một lớp có tên AlternativeParent kéo dài Chánh

public class AlternativeParent extends Parent {} 

Bây giờ lập bản đồ của mình (Thông báo Child là yếu tố tổng hợp thay vì @Entity đồng bằng)

<class name="AlternativeParent" table="PARENT"> 
    <id name="id"> 
     <generator class="native"/> 
    </id> 
    <bag name="childList" table="CHILD"> 
     <key column="PARENT_ID" not-null="false"/> 
     <composite-element class="Child"> 
      <property column="CHILD_ID" name="id"/> 
     </composite-element> 
    </bag> 
</class> 

Bây giờ thực hiện một thuận tiện bằng phương thức trong lớp Child

public boolean equals(Object o) { 
    if (!(o instanceof Child)) 
     return false; 

    Child other = (Child) o; 
    // identity equality 
    // Used by composite elements 
    if(getId() != null) { 
     return new EqualsBuilder() 
        .append(getId(), other.getId()) 
        .isEquals(); 
    } else { 
     // object equality 
    } 
} 

Nếu tôi Refactor trường hợp thử nghiệm hiển thị ở trên (Bây giờ bằng cách sử dụng AlternativeParent thay)

@Test 
public void removed_second_from_parent_remove_second_from_database() { 
    AlternativeParent parent = new AlternativeParent(); 
    parent.setId((Integer) parentId); 

    Child first = new Child(); 
    first.setId((Integer) firstId); 

    /** 
     * It simulates the second one has been removed 
     */ 
    parent.getChildList().add(first); 

    Session session = sessionFactory.openSession(); 
    session.beginTransaction(); 

    session.update(parent); 

    session.getTransaction().commit(); 
    session.close(); 

    session = sessionFactory.openSession(); 
    session.beginTransaction(); 

    Child second = (Child) session.get(Child.class, secondId); 
    Assert.assertNull(second); 

    session.getTransaction().commit(); 
    session.close(); 

} 

tôi nhìn thấy một thanh màu xanh

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