2009-07-30 17 views
8

Mam ten problem od dłuższego czasu, przeszukałem sieć i SO i obecnie i nie znalazłem jeszcze rozwiązania. Mam nadzieję, że możesz mi w tym pomóc.Hibernate @OneToMany z mappedBy (rodzic-dziecko) relacja i problem z pamięcią podręczną

Mam relacji rodzic-dziecko pomiędzy dwoma podmiotami, jak następuje:

@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; 

    // ... 
} 

Rzeczą jest, że gdy tworzę nowe dziecko i przypisać go do rodzica, rodzic nie jest aktualizowany, gdy jest już w pamięci podręcznej.

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

// ... 

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

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

Próbowałem użyć @PreUpdate aby automatycznie dodać dziecka do rodzica, gdy dziecko jest zachowywane, ale w przypadku, gdy mamy 2 menedżerów podmiotem w 2 różnych wątków (jak w JBoss), problem nadal istnieje, dopóki nie zadzwonimy pod numer em.refresh(parent)

Pytanie brzmi - czy istnieje sposób na płynne wyeliminowanie problemu i zapewnienie, że parent.getChildren() zawsze zwraca aktualną listę dzieci?

Odpowiedz

8

Większość ORM zachowa się w ten sposób.

Obiekt w pamięci podręcznej nie jest aktualizowany z bazy danych (dodatkowy odczyt, który nie jest konieczny). Pomyśl także o modelu obiektowym i trwałości jako oddzielnych. tzn. zachowaj spójność modelu obiektowego z samym sobą i nie polegaj na mechanizmie trwałości, aby zrobić to za Ciebie.

Jeśli chcesz, aby obiekt został dodany do kolekcji, zrób to w kodzie "setParent".

Najlepsza praktyka w tym przypadku polega na tym, aby jedna strona związku wykonywała całą pracę i pozwoliła drugiej stronie odłożyć się na nią. Sugerowałbym również użycie dostępu do pola zamiast dostępu do metody, w ten sposób można dostosować metody z większą elastycznością.

Dodaj metodę rodzica nazwie addChild

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

a następnie dokonać setParent w dziecko:

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

setParent0 u dziecka jest stter nieruchomości dla rodzica na dziecko.

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

Chciałbym również zasugerować, że „getChildren” metoda zwróci niezmienny zbiór tak, że deweloperzy nie nie inadvertantly użyć tej metody (dowiedziałem się na własnej skórze, w tym wszystkim).

Jeszcze jedno, powinieneś mieć zerową kontrolę kodu i innych elementów obronnych w powyższym kodzie, zostawiłem to dla jasności.

+0

Dziękujemy za obszernej odpowiedzi, Michael. Znalazłem w nim dobre informacje. Obawiam się jednak, że nie rozwiązuje problemu, który mam, ponieważ dwie różne instancje EntityManager przechowują dwie różne pamięci podręczne, a gdy jedna z nich aktualizuje instancję jednostki, druga nie widzi aktualizacji, a jej obiekt w pamięci podręcznej staje się nieaktualny. – artemb

+0

Wygląda na to, że musisz spojrzeć na wyzwalacze aktualizacji, które następnie wezmą ten obiekt i zaktualizują inną pamięć podręczną. Lub możesz uczynić te dwa elementy pamięci podręcznej członkami tego samego klastra, jeśli buforujesz rozwiązanie wspomagające tworzenie klastrów. –

+0

Niestety nie mam kontroli nad pamięcią podręczną sesji Hibernate. A może ja? – artemb

1

Jeśli chodzi o problem z buforowaniem, jest to bardzo powszechny problem, gdy wiele maszyn wirtualnych działa w tej samej bazie danych z oddzielnymi pamięciami podręcznymi. To się nazywa "dryf pamięci podręcznej".

Większość implementacji pamięci podręcznej przyjaznych hibernacji (ehcache, OSCache i SwarmCache) ma wbudowane rozproszone pamięci podręcznej, które może być używane do synchronizowania pamięci podręcznych. Rozpakowana pamięć podręczna generalnie wysyła wiadomości multiemisji aktualizujące stan pamięci podręcznej. Wykonywanie eksmisji drugiego poziomu pamięci podręcznej przez SessionFactory.evict (Class, id), na przykład, spowoduje wysłanie komunikatu o unieważnieniu do innych pamięci podręcznych w klastrze, co unieważni wszelkie inne kopie tego obiektu w innych pamięciach podręcznych.

W zależności od wdrożenia, multiemisja może lub nie może być dla ciebie akceptowalna. Jeśli tak nie jest, może być konieczne użycie rozwiązania z pojedynczą pamięcią podręczną, np. Memcached.

Osobiście odkryłem, że konfiguracja rozproszonego bufora podręcznego eh jest bardzo prosta.

EH cache omawia problem w nieco bardziej szczegółowo: http://ehcache.org/documentation/distributed_caching.html

4

Dość, że Twój problem polega na tym ustawienia Cascade.

@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; 

    // ... 
} 

Stosując te ustawienia kaskada kaskada będzie się utrzymywać i aktualizacje do obiektów podrzędnych.

np.

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 

lub

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

child.getParent(); // returns the parent 

Więcej informacji na ten temat można znaleźć na stronie Hibernate Annotations

Powiązane problemy