Гибернация, однонаправленный ManyToOne и желание использовать каскадную функцию при удалении

У меня проблема, аналогичная представленной здесь: как определить обратное каскадное удаление для сопоставления" многие-к-одному "в спящем режиме

После некоторого поиска я не могу найти для этого достойного / чистого решения. Я не могу, чтобы родительский объект имел @OneToMany для дочернего объекта, потому что они находятся в разных модулях. Я хотел попробовать EntityListener, который удалял бы потомков раньше, чем родительский, но я не могу, потому что, опять же, они находятся в разных модулях.

Кто-нибудь знает чистое решение для этого? Я подумываю использовать AspectJ для прослушивания вызова метода удаления из ParentDao, но это не чистое решение, и мне придется реализовать его для каждой сущности, которая имеет такое отношение к родительскому классу.

Такой каскад кажется базовой функцией, и я разочарован тем, что спящий режим не поддерживает его: /


person Caesar Ralf    schedule 05.12.2011    source источник
comment
Ваша ссылка указывает на вопрос о самой важной книге, которую вы прочитали. Ничего общего с Hibernate. Объясните, пожалуйста, вашу проблему в этом вопросе.   -  person JB Nizet    schedule 05.12.2011
comment
Извините, связал не в том месте. Редактируем ссылку.   -  person Caesar Ralf    schedule 05.12.2011


Ответы (2)


Ответ на вопрос, на который вы указали, правильный. Hibernate может удалять дочерние элементы только при удалении родителя, если родитель знает о своих дочерних элементах.

Единственное решение - использовать метод удаления ParentDAO для поиска всех дочерних элементов родительского элемента, удаления их, а затем удаления самого родительского элемента.

Если вас беспокоит, что ParentDAO не должен знать о дочерних элементах, вы можете сделать его разделенным, а у ParentDAO будет список зарегистрированных ParentDeletionListeners, которые будут вызываться перед удалением самого родителя. ParentDAO знает только об этом интерфейсе ParentDeletionListener и позволяет регистрировать несколько слушателей. При запуске приложения зарегистрируйте слушателя для каждого типа потомков и попросите слушателя удалить потомков:

public interface ParentDeletionListener {
    void parentWillBeDeleted(Parent parent);
}

public class SomeChildParentDeletionListener implements ParentDeletionListener {
    // ...
    public void parentWillBeDeleted(Parent parent) {
        // search for every SomeChild linked to the given parent
        // and delete them
    }
}

public class ParentDAO {
    private List<ParentDeletionListener> listeners = new CopyOnWriteArrayList();

    public void addParentDeletionListener(ParentDeletionListener listener) {
        this.listeners.add(listener);
    }

    public void deleteParent(Parent p) {
        for (ParentDeletionListener listener : listeners) {
            listener.parentWillBeDeleted(parent);
        }
        session.delete(parent);
    }
}
person JB Nizet    schedule 05.12.2011
comment
Это другой подход к методу EntityListener # preDelete. Это хорошее решение, но меня беспокоит, что нам нужно делать что-то подобное в спящем режиме для решения такой типичной проблемы. В любом случае спасибо за ответ :) - person Caesar Ralf; 05.12.2011
comment
Вы вынуждены сделать это, потому что решили не связывать родителя с детьми. - person JB Nizet; 05.12.2011
comment
Я нашел общий способ сделать это. Я отправлю его, когда мне позволит stackoverflow. Спасибо за идею JB :) - person Caesar Ralf; 05.12.2011

Основываясь на ответе JB Nizet, я изменил свой DAO на DeleteOperationListener (моя базовая реализация DAO основана на «Не повторяйте DAO» [1].) Таким образом, у меня есть общее решение на случай, если я окажусь в том же ситуация снова. Структура выглядит так:

public interface GenericDao<T, PK extends Serializable> {
    // CRUD methods

    // delete operation listeners.
    void addDeleteListener(DeleteOperationListener<T, PK> deleteOperationListener);

    public interface DeleteOperationListener<T> {
        void preDelete(T entity);
        void posDelete(T entity);
    }
}

И моя абстрактная реализация спящего режима я могу уведомить наблюдателей об удалении.

@Override
public void delete(T entityToDelete) {
    notifyPreDelete(entityToDelete);
    this.getHibernateTemplate().delete(entityToDelete);
    notifyPosDelete(entityToDelete);
}

И теперь у меня есть другой класс, который обрабатывает удаление дочерних элементов без необходимости изменять DAO:

@Service
public class ParentModificationListener
    implements GenericDao.DeleteOperationListener<Parent> {

    private ChildDao childDao;

    @Autowired
    public ParentModificationListener(ChildDao childDao, ParentDao parentDao) {
        this.childDao = childDao;
        parentDao.addDeleteListener(this);
    }

    @Override
    public void preDelete(Parent parent) {
        this.childDao.deleteChildrenFromParent(parent);
    }

    @Override
    public void posDelete(Parent parent) {
        // DO NOTHING
    }
}

[1] http://www.ibm.com/developerworks/java/library/j-genericdao.html

person Caesar Ralf    schedule 06.12.2011