Гибернация @OneToMany с привязкой mappedBy (родитель-потомок) и проблемой кэша

StackOverflow https://stackoverflow.com/questions/1206452

Вопрос

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

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

@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() всегда возвращать актуальный список дочерних элементов?

Это было полезно?

Решение

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

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

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

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

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

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

, а затем установите setParent в Child:

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

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

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

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

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

Другие советы

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

@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

более подробную информацию об этом можно найти по адресу Примечания к переходу в спящий режим

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top