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