2010-10-21 5 views
15

Я знаю, что удаление дочерних объектов-сирот является распространенным вопросом о SO и общей проблемой для людей, новых для Hibernate, и что довольно стандартный ответ заключается в том, чтобы у вас есть некоторые варианты cascade=all,delete-orphan или cascade=all-delete-orphan о детской коллекции.Может ли Hibernate удалять потерянные коллекции при обновлении отдельного объекта?

Я бы хотел, чтобы Hibernate обнаружил, что дочерняя коллекция была опустошена/удалена из родительского объекта, и в случае, когда родительский объект обновляется, удаляются строки из дочерней таблицы из базы данных. Например:

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

Мое текущее отображение для Parent класса выглядит следующим образом:

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

Это прекрасно работает для меня при обновлении вложенного объекта, но у меня есть случай использования, в котором мы хотели бы (который был отправлен нашему API-методу удаленным клиентом по протоколу HTTP/JSON) и передать его непосредственно на сеанс Hibernate - чтобы клиенты могли манипулировать родительским объектом каким бы то ни было способом им нравятся и изменения сохраняются.

При вызове session.update(parent) на мой выделенный объект строки в дочерней таблице осиротевшие (столбец FK установлен в null), но не удаляются. Обратите внимание, что когда я звоню session.update(), это первый сеанс Hibernate Session, который видит этот экземпляр объекта - я не присоединяю или не объединяю объект с сеансом каким-либо другим способом. Я полагаюсь на клиента, чтобы передать объекты, идентификаторы которых соответствуют реальным объектам в базе данных. Например, логика в моем методе обслуживания API-то вроде этого:

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

Возможно ли Hibernate для обнаружения осиротевших детей коллекций в отдельных родительских объектах при передаче session.update(parent)? Или я неправильно использую отдельный объект?

Моя надежда состояла в том, что я мог избежать любых сложных взаимодействий с Hibernate, чтобы сохранить изменения в отдельном экземпляре. Мой API-метод не нуждается в дальнейшем изменении отдельного объекта после вызова до session.update(parent), этот метод просто отвечает за постоянные изменения, сделанные удаленными клиентскими приложениями.

ответ

1

Думаю, при использовании отдельной сессии вы можете столкнуться с проблемой, с коллекциями. я предлагаю вам сначала загрузить объект с коллекцией, а затем обновить этот объект с изменениями, которые помогут.

+0

вы имеете в виду, загрузить существующий объект, а затем 'слияния () 'он с отсоединенным экземпляром передал мне API? –

+0

@ mattb: вы можете написать логику для объединения коллекции или даже просто заменить предыдущую коллекцию и установить новую, но убедитесь, что вы добавили тег в свой hbm. Это будет содержать простой запрос удаления sql для удаления полной коллекции перед сохранением новой. или другими способами, если вы добавите что-нибудь, этот sql-delete будет запущен каждый раз, а затем будет добавлена ​​новая копия вашей коллекции. Это может решить вашу проблему при каждом обновлении коллекции вручную. –

+0

Не уверен, что мне нравится это решение, так как это потребует изменения моего шаблона использования - например, ''. Надеюсь, это можно было бы зафиксировать только с помощью картографирования. –

8

Ваше отображение (упрощенный)

<?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> 

производит

PARENT 
    ID 

CHILD 
    CHILD_ID 
    PARENT_ID 

По, что вы сказали

Я хотел бы иметь возможность Hibernate обнаружить, что сбор ребенка был удален с родительский объект, и в строки в дочерней таблице удаляются из базы данных , когда родительский объект обновляется

Что-то вроде

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

session.update(parent); 

Вы сказали, что это работает отлично , потому что у вас есть прилагаемая родительская инстанция

Теперь давайте посмотрим следующий (Уведомление Assert.assertNull (second))

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(); 
    } 
} 

К сожалению, тест не проходят. Что ты можешь сделать ???

  • Включение затянувшегося разговора

Hibernate ссылка говорит

Extended (или Long) Session - The Hibernate Session может быть отсоединен от основного соединения JDBC после транзакции базы данных был зафиксирован и повторно подключен при возникновении нового запроса клиента. Этот шаблон известен как сеанс для разговора и делает невозможным повторную привязку. Автоматическое ведение версий используется для изоляции одновременных изменений, и сеансу обычно не разрешается автоматически очищаться, но явно.

отказ от ответственности: у меня нет сценариев, в которых используется продолжительная беседа. Java EE Голосовые сеансовые компоненты поддерживают длительную беседу. Но его поддержка для JPA (не Hibernate)

Или Вы можете создать альтернативное отображение, которое позволит вашему ребенку как составные элементы.Поскольку его жизненный цикл зависит от родительского объекта, вы можете рассчитывать на составных элементах, чтобы получить то, что вы хотите

Создать класс с именем AlternativeParent , который проходит Родитель

public class AlternativeParent extends Parent {} 

Теперь его отображение (уведомление ребенка в качестве составного элемента вместо равнины @Entity)

<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> 

Теперь реализовать удобный метод равных в классе ребенка

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 
    } 
} 

Если я реорганизовать тестовый пример, показанный выше (Теперь с помощью AlternativeParent вместо)

@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(); 

} 

Я вижу зеленую полосу

 Смежные вопросы

  • Нет связанных вопросов^_^