Hibernate @OneToMany с отношениями mappedBy (parent-child) и проблемой кэширования - PullRequest
8 голосов
/ 30 июля 2009

У меня есть эта проблема в течение долгого времени, я искал в Интернете и SO, и не нашел решения до сих пор. Я надеюсь, что вы можете помочь мне в этом.

У меня есть отношения родитель-ребенок между двумя сущностями, подобные следующим:

@Entity
public class Parent {
    // ...

    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
    private Set<Child> children = new HashSet<Child>();

    // ...
}

@Entity
public class Child {
    // ...

    @ManyToOne(fetch = FetchType.LAZY)
    private Parent parent;

    // ...
}

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

 Parent parent = new Parent();
 em.persist(parent);

 // ...

 Child child = new Child();
 child.setParent(parent);
 em.persist(child);

 parent.getChildren().size(); // returns 0

Я пытался использовать @PreUpdate для автоматического добавления дочернего элемента в родительский, когда дочерний элемент сохраняется, но в случае, когда у нас есть 2 менеджера сущностей в 2 разных потоках (как в JBoss), проблема все еще существует, пока мы звоним em.refresh(parent)

Итак, вопрос в том, есть ли способ плавно устранить проблему и обеспечить, чтобы parent.getChildren() всегда возвращал обновленный список детей?

Ответы [ 3 ]

8 голосов
/ 30 июля 2009

Большинство ORM будут вести себя таким образом.

Объект в кэше не обновляется из базы данных (дополнительное чтение не требуется). Также думайте об объектной модели и постоянстве как об отдельном. Т.е. держите вашу объектную модель в согласии с самим собой и не полагайтесь на механизм постоянства, чтобы сделать это за вас.

Итак, если вы хотите, чтобы объект был добавлен в коллекцию, сделайте это в коде "setParent".

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

Добавить метод к родителю с именем addChild

 public void addChild(Child child) {
    child.setParent0(this);
    getChildren().add(individualNeed);
 }

, а затем сделать setParent in Child:

public void setParent(Parent parent) {
   parent.addChild(child);
}

setParent0 in Child - это свойство stter для parent для child.

public void setParent0(Parent parent) {
   this.parent = parent;
}

Я бы также предложил, чтобы метод "getChildren" возвращал неизменную коллекцию, чтобы разработчики случайно не использовали этот метод (я научился всему этому нелегко).

Еще одна вещь, у вас должен быть нулевой код проверки и другие защитные элементы в приведенном выше коде, я оставил это для ясности.

4 голосов
/ 28 сентября 2011

Уверен, что ваша проблема здесь в настройках каскада.

@Entity
public class Parent {
   // ...

   @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, 
      cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
   @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
   private Set<Child> children = new HashSet<Child>();

   // ...
}

@Entity
public class Child {
    // ...

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
    private Parent parent;

    // ...
}

Использование этих настроек каскада приведет к сохранению каскада и обновлению дочерних объектов.

например.

Parent parent = new Parent();
em.persist(parent);

// ...

Child child = new Child();
child.setParent(parent);
em.persist(child); //will cascade update to parent

parent.getChildren().size(); // returns 1

или

Parent parent = new Parent();
Child child = new Child();
parent.setChild(parent);
em.persist(parent); //will cascade update to child

child.getParent(); // returns the parent

Более подробную информацию об этом можно найти на Аннотации Hibernate

1 голос
/ 16 ноября 2010

Что касается вашей проблемы с кэшированием, то это очень распространенная проблема, когда у вас несколько виртуальных машин, работающих с одной и той же базой данных, с отдельными кэшами. Это называется "дрейф кеша".

Большинство реализаций кэша, дружественных к hibernate (ehcache, OSCache и SwarmCache), имеют встроенный распределенный кеш, который можно использовать для синхронизации кешей. Распределенный кеш, как правило, отправляет многоадресные сообщения, обновляющие состояние кеша. Например, выполнение извлечения кэша второго уровня с помощью SessionFactory.evict (Class, id) приведет к тому, что сообщение о недействительности будет отправлено на другие кэши в кластере, что сделает недействительными любые другие копии этого объекта в других кэшах.

В зависимости от вашего развертывания, многоадресная рассылка может быть или не быть приемлемой для вас. Если это не так, вам может потребоваться использовать решение с одним кэшем, например memcached.

Мне лично показалось, что конфигурация распределенного кэша eh cache очень проста.

Кэш EH обсуждает проблему более подробно здесь: http://ehcache.org/documentation/distributed_caching.html

...