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?


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ż:


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 .

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.


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.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.