Jaka jest różnica między jednokierunkowymi i dwukierunkowymi skojarzeniami JPA i hibernacją?

135

Jaka jest różnica między asocjacjami jednokierunkowymi i dwukierunkowymi?

Ponieważ wszystkie tabele wygenerowane w bazie danych są takie same, jedyną różnicą, jaką znalazłem, jest to, że każda strona dwukierunkowych powiązań będzie miała odniesienie do drugiej, a jednokierunkowa nie.

To jest skojarzenie jednokierunkowe

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}

public class Group {
    private int     id;
    private String  name;
}

Skojarzenie dwukierunkowe

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}
public class Group {
    private int         id;
    private String      name;
    @OneToMany(mappedBy="group")
    private List<User>  users;
}

Różnica polega na tym, czy grupa posiada referencję użytkownika.

Więc zastanawiam się, czy to jedyna różnica? co jest zalecane?

hguser
źródło
7
Grupa będzie teraz wiedzieć, których użytkowników zawiera. Nie sądzę, żeby to była w jakikolwiek sposób mała różnica.
Satadru Biswas
5
Relacje dwukierunkowe stały się dla mnie chaosem, jeśli chodzi o aktualizację. :)
diyoda_

Odpowiedzi:

152

Główną różnicą jest to, że relacja dwukierunkowa zapewnia dostęp nawigacyjny w obu kierunkach, dzięki czemu można uzyskać dostęp do drugiej strony bez wyraźnych zapytań. Pozwala również na zastosowanie opcji kaskadowych w obu kierunkach.

Należy zauważyć, że dostęp nawigacyjny nie zawsze jest dobry, szczególnie w przypadku relacji „jeden do bardzo wielu” i „wiele do bardzo wielu”. Wyobraź sobie, Groupże zawiera tysiące Users:

  • Jak masz do nich dostęp? Przy tak wielu Usersach zwykle trzeba zastosować filtrowanie i / lub paginację, tak że i tak trzeba wykonać zapytanie (chyba że używasz filtrowania kolekcji , co dla mnie wygląda na hack). Niektórzy programiści mogą mieć w takich przypadkach tendencję do stosowania filtrowania w pamięci, co oczywiście nie jest dobre dla wydajności. Zauważ, że posiadanie takiej relacji może zachęcić programistów tego rodzaju do korzystania z niej bez uwzględnienia wpływu na wydajność.

  • Jak dodasz nowe Users do Group? Na szczęście Hibernate patrzy na stronę właściciela relacji, gdy ją utrzymuje, więc możesz tylko ustawić User.group. Jednakże, jeśli chcesz zachować obiekty w pamięci spójne, trzeba także dodać Userdo Group.users. Ale to spowodowałoby, że Hibernate mógłby pobrać wszystkie elementy Group.usersz bazy danych!

Dlatego nie mogę zgodzić się z zaleceniem zawartym w Najlepszych praktykach . Musisz starannie zaprojektować relacje dwukierunkowe, biorąc pod uwagę przypadki użycia (czy potrzebujesz dostępu nawigacyjnego w obu kierunkach?) I możliwe konsekwencje dla wydajności.

Zobacz też:

axtavt
źródło
Cześć, dzięki, wygląda na to, że jesteś eksperymentatorem hibernacji, skoro odpowiedziałeś na moje pytanie: stackoverflow.com/questions/5350770/… . A teraz nie jestem pewien co się trzyma, bo więcej nie mogę napisać w komentarzu, więc zamieszczam go tutaj dpaste.de/J85m. Proszę o sprawdzenie jeśli to możliwe. :)
hguser
@hguser: Jeśli w końcu zdecydujesz się uczynić swoją relację dwukierunkową, myślę, że lepiej byłoby zadzwonić setGroup()z addUser(), aby zachować spójność obu stron.
axtavt
a co jeśli nie wywołam metody setGroup () wewnątrz group.addUser ()?
hguser
@hguser: Związek nie zostałby utrwalony, patrz punkt 2 odpowiedzi. Możesz dzwonić setGroup()bez addUser(), ale spowodowałoby to niespójny stan obiektów w pamięci.
axtavt
Okazało się, że nie mogę poprawnie mapować, czy możesz poświęcić trochę czasu na sprawdzenie mojego projektu na githubie? to jest mały projekt.
hguser
31

Istnieją dwie główne różnice.

Dostęp do stron stowarzyszenia

Pierwsza dotyczy tego, w jaki sposób uzyskasz dostęp do związku. W przypadku asocjacji jednokierunkowej możesz nawigować po asocjacji tylko z jednego końca.

Tak więc w przypadku @ManyToOneskojarzenia jednokierunkowego oznacza to, że dostęp do relacji można uzyskać tylko od strony podrzędnej, w której znajduje się klucz obcy.

Jeśli masz @OneToManyskojarzenie jednokierunkowe , oznacza to, że możesz uzyskać dostęp do relacji tylko od strony nadrzędnej, gdzie znajduje się klucz obcy.

W przypadku @OneToManyasocjacji dwukierunkowej możesz nawigować po asocjacji na oba sposoby, od strony rodzica lub strony podrzędnej.

Należy również użyć metod narzędzi dodawania / usuwania dla skojarzeń dwukierunkowych, aby upewnić się, że obie strony są prawidłowo zsynchronizowane .

Występ

Drugi aspekt dotyczy wydajności.

  1. Dla @OneToMany, jednokierunkowe stowarzyszenia nie wykonują, jak również te, które dwukierunkowych .
  2. Na przykład @OneToOne, dwukierunkowe powiązanie spowoduje, że rodzic będzie chętnie pobierany, jeśli Hibernate nie może określić, czy należy przypisać serwer proxy, czy wartość zerową .
  3. Na @ManyToMany, typ kolekcji sprawia, że dość różnica jak Setswykonać lepiej niżLists .
Vlad Mihalcea
źródło
11

Pod względem kodowania relacja dwukierunkowa jest bardziej złożona do wdrożenia, ponieważ aplikacja jest odpowiedzialna za utrzymywanie synchronizacji obu stron zgodnie ze specyfikacją JPA 5 (na stronie 42). Niestety przykład podany w specyfikacji nie podaje więcej szczegółów, więc nie daje wyobrażenia o poziomie złożoności.

Jeśli nie używasz pamięci podręcznej drugiego poziomu, zazwyczaj nie jest problemem, jeśli metody relacji są poprawnie zaimplementowane, ponieważ instancje są odrzucane na koniec transakcji.

Podczas korzystania z pamięci podręcznej drugiego poziomu, jeśli cokolwiek zostanie uszkodzone z powodu nieprawidłowo zaimplementowanych metod obsługi relacji, oznacza to, że inne transakcje również będą widzieć uszkodzone elementy (pamięć podręczna drugiego poziomu jest globalna).

Prawidłowo zaimplementowana relacja dwukierunkowa może uprościć zapytania i kod, ale nie należy jej używać, jeśli nie ma to sensu z punktu widzenia logiki biznesowej.

Constantino Cronemberger
źródło
9

Nie jestem w 100% pewien, że to jedyna różnica, ale to główna różnica. Zaleca się również, aby asocjacje dwukierunkowe były opisane w dokumentacji Hibernate:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html

Konkretnie:

Preferuj asocjacje dwukierunkowe: Powiązania jednokierunkowe są trudniejsze do przeszukania. W dużej aplikacji w zapytaniach prawie wszystkie asocjacje muszą umożliwiać nawigację w obu kierunkach.

Osobiście mam mały problem z tą ogólną rekomendacją - wydaje mi się, że są przypadki, w których dziecko nie ma żadnego praktycznego powodu, aby wiedzieć o swoim rodzicu (np. Dlaczego pozycja zamówienia musi wiedzieć o kolejności, w jakiej jest związane z?), ale widzę w tym wartość również w rozsądnej części czasu. A ponieważ dwukierunkowość tak naprawdę niczego nie szkodzi, nie uważam, aby jej przestrzeganie było zbyt niepożądane.

kvista
źródło
Dwukierunkowość może w niektórych przypadkach boleć, jak wyjaśnił axtavt! Byłbym bardzo ostrożny z każdą relacją odwzorowaną w encji Hibernate! Możesz skończyć z nieskończonym czasem potrzebnym do załadowania rzeczy w Hibernacji, chyba że wiesz dokładnie, co robisz. Dokładnie przemyśl przypadki użycia i użyj różnych klas modeli dla różnych przypadków użycia. F.ex. na liście rzeczy nie potrzebuję wszystkich powiązanych obiektów, wystarczy etykieta i identyfikator. Zatem dla mnie encja listy jest inna (i bardzo prosta) niż encja szczegółowa.
cslotty