Hibernate, однонаправленный ManyToOne и стремление к "Каскадному удалению"

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

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

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

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

2 ответа

Решение

Основываясь на ответе 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

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

Единственное решение - использовать метод delete 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);
    }
}
Другие вопросы по тегам