Qual è la differenza tra le associazioni unidirezionali e bidirezionali di JPA e Hibernate?


135

Qual è la differenza tra le associazioni unidirezionali e bidirezionali?

Poiché la tabella generata nel db è la stessa, quindi l'unica differenza che ho riscontrato è che ciascun lato delle associazioni bidirezionali avrà un riferimento all'altro e il non unidirezionale.

Questa è un'associazione unidirezionale

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

L'associazione bidirezionale

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

La differenza è se il gruppo detiene un riferimento dell'utente.

Quindi mi chiedo se questa è l'unica differenza? quale è raccomandato?


7
Il gruppo ora saprà quali utenti contiene. Non penso che questa sia una piccola differenza.
Satadru Biswas,

5
Le relazioni bidirezionali sono diventate un caos per me quando si tratta di aggiornamento. :)
diyoda_

Risposte:


152

La differenza principale è che la relazione bidirezionale fornisce l'accesso alla navigazione in entrambe le direzioni, in modo da poter accedere dall'altra parte senza richieste esplicite. Inoltre, consente di applicare le opzioni a cascata in entrambe le direzioni.

Si noti che l'accesso alla navigazione non è sempre buono, specialmente per le relazioni "da uno a moltissimi" e "da molti a moltissimi". Immagina un Groupche contiene migliaia di Users:

  • Come vorresti accedervi? Con così tanti messaggi User, di solito è necessario applicare alcuni filtri e / o impaginazione, quindi è necessario eseguire comunque una query (a meno che non si utilizzi il filtro di raccolta , che per me sembra un trucco). Alcuni sviluppatori potrebbero tendere ad applicare il filtro in memoria in questi casi, il che ovviamente non è buono per le prestazioni. Si noti che avere una relazione del genere può incoraggiare questo tipo di sviluppatori a usarla senza considerare le implicazioni delle prestazioni.

  • Come aggiungeresti nuovi Users al Group? Fortunatamente, Hibernate osserva il lato proprietario della relazione quando persiste, quindi puoi solo impostare User.group. Tuttavia, se si desidera mantenere coerenti gli oggetti in memoria, è necessario aggiungere Useranche Group.users. Ma renderebbe Hibernate a recuperare tutti gli elementi Group.usersdal database!

Quindi, non posso essere d'accordo con la raccomandazione delle migliori pratiche . È necessario progettare attentamente le relazioni bidirezionali, considerando i casi d'uso (è necessario l'accesso alla navigazione in entrambe le direzioni?) E le possibili implicazioni sulle prestazioni.

Guarda anche:


Ciao, grazie, sembra che tu sia un esperto di ibernazione, dal momento che hai risposto alla mia domanda: stackoverflow.com/questions/5350770/… . E ora non sono sicuro del mantenimento della relazione, dal momento che non posso scrivere altro nel commento, quindi lo pubblico qui dpaste.de/J85m . Si prega di controllare se possibile. :)
hguser

@hguser: Se finalmente si decide di farvi relazione bidirezionale, penso che sarebbe meglio chiamare setGroup()da addUser()al fine di tenere entrambi i lati coerente.
axtavt,

che ne dici se non chiamo setGroup () all'interno di group.addUser ()?
hguser

@hguser: la relazione non sarebbe persistente, vedere il punto 2 nella risposta. È possibile chiamare setGroup()senza addUser(), ma ciò comporterebbe uno stato incoerente di oggetti in memoria.
axtavt,

Ho scoperto che non riesco a mappare correttamente, puoi risparmiare un po 'di tempo per controllare il mio progetto su github? è un piccolo progetto.
hguser,

31

Ci sono due differenze principali.

Accesso ai lati dell'associazione

Il primo è legato a come accederai alla relazione. Per un'associazione unidirezionale, è possibile navigare nell'associazione solo da un'estremità.

Quindi, per un'associazione unidirezionale @ManyToOne, significa che puoi accedere alla relazione solo dal lato figlio in cui risiede la chiave esterna.

Se si dispone di un'associazione unidirezionale @OneToMany, significa che è possibile accedere alla relazione solo dalla parte padre in cui risiede la chiave esterna.

Per l' @OneToManyassociazione bidirezionale , è possibile navigare nell'associazione in entrambi i modi, dal lato genitore o dal lato figlio.

È inoltre necessario utilizzare i metodi di utilità Aggiungi / Rimuovi per le associazioni bidirezionali per assicurarsi che entrambe le parti siano sincronizzate correttamente .

Prestazione

Il secondo aspetto è legato alle prestazioni.

  1. Infatti @OneToMany, le associazioni unidirezionali non si comportano bene come quelle bidirezionali .
  2. Infatti @OneToOne, un'associazione bidirezionale causerà il recupero impaziente del genitore se Hibernate non è in grado di dire se il proxy deve essere assegnato o un valore nullo .
  3. Perché @ManyToMany, il tipo di raccolta fa la differenza, poiché Setsfunziona meglio diLists .

11

In termini di codifica, una relazione bidirezionale è più complessa da implementare perché l'applicazione è responsabile di mantenere entrambe le parti in sincronia in base alla specifica JPA 5 (a pagina 42). Purtroppo l'esempio fornito nelle specifiche non fornisce ulteriori dettagli, quindi non dà un'idea del livello di complessità.

Quando non si utilizza una cache di secondo livello, di solito non è un problema non avere i metodi di relazione implementati correttamente perché le istanze vengono scartate al termine della transazione.

Quando si utilizza la cache di secondo livello, se qualcosa viene danneggiato a causa di metodi di gestione delle relazioni implementati in modo errato, ciò significa che anche altre transazioni vedranno gli elementi danneggiati (la cache di secondo livello è globale).

Una relazione bidirezionale correttamente implementata può semplificare le query e il codice, ma non dovrebbe essere utilizzata se non ha davvero senso in termini di logica aziendale.


9

Non sono sicuro al 100% che questa sia l' unica differenza, ma è la differenza principale . Si raccomanda inoltre di avere associazioni bidirezionali dai documenti di Hibernate:

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

In particolare:

Preferisci associazioni bidirezionali: le associazioni unidirezionali sono più difficili da interrogare. In un'applicazione di grandi dimensioni, quasi tutte le associazioni devono essere navigabili in entrambe le direzioni nelle query.

Personalmente ho un leggero problema con questa raccomandazione generale - mi sembra che ci siano casi in cui un bambino non ha alcun motivo pratico per conoscere il suo genitore (ad esempio, perché un articolo di ordine deve sapere dell'ordine che è associato a?), ma vedo valore in esso anche una parte ragionevole del tempo. E dal momento che la bidirezionalità non fa davvero male a nulla, non trovo troppo discutibile aderire.


La bidirezionalità può danneggiare in alcuni casi, come ha spiegato axtavt! Starei molto attento con ogni relazione mappata in un'entità ibernata! Puoi finire con il tempo infinito necessario per caricare le cose in Hibernate, a meno che tu non sappia esattamente cosa stai facendo. Pensa attentamente ai casi d'uso e usa classi di modello diverse per casi d'uso diversi. F.ex. in un elenco di cose, non ho bisogno di tutti gli oggetti correlati, solo un'etichetta e l'ID. Quindi l'entità elenco è diversa (e molto semplice) dall'entità dettaglio, per me.
cslotty,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.