Salvataggio di ManyToMany bidirezionale
Domanda
Ho due classi di entità annotate nel modo seguente
@Entity
class A {
@ManyToMany(mappedBy="A", cascade=CascadeType.ALL)
private List<B> b;
..
}
@Entity
class B {
@ManyToMany(cascade=CascadeType.ALL)
private List<A> a;
..
}
Se memorizzo un'istanza della classe 'B', le relazioni vengono archiviate nel database e il getter nella classe 'A' restituirà il sottoinsieme corretto di B. Tuttavia, se apporto modifiche all'elenco di Bs in "A", le modifiche non vengono memorizzate nel database?
La mia domanda è: come posso fare in modo che i cambiamenti in entrambe le classi siano "quotati" " all'altra classe?
EDIT: ho provato diverse varianti di rimozione del parametro mappedBy e di definizione di JoinTable (e colonne), ma non sono stato in grado di trovare la combinazione corretta.
Soluzione
La risposta più breve sembra essere impossibile e ha senso. In un'associazione bidirezionale bidirezionale un lato deve essere master e viene utilizzato per mantenere le modifiche alla tabella di join sottostante. Poiché JPA non mantiene entrambi i lati dell'associazione, è possibile che si verifichi una situazione di memoria che non può essere ricaricata una volta archiviata nel database. Esempio:
A a1 = new A();
A a2 = new A();
B b = new B();
a1.getB().add(b);
b.getA().add(a2);
Se questo stato potesse essere persistito, si finirebbe con le seguenti voci nella tabella di join:
a1_id, b_id
a2_id, b_id
Ma al momento del caricamento, come farebbe JPA a sapere che intendevi comunicare a b solo a2 e non a1? e che dire di a2 che non dovrebbe sapere di b?
Questo è il motivo per cui devi mantenere tu stesso l'associazione bidirezionale (e rendere impossibile l'esempio di cui sopra, anche in memoria) e JPA persisterà solo in base allo stato di un suo lato.
Altri suggerimenti
Hai specificato le colonne di join inverse?
@Entity
class A {
@ManyToMany(mappedBy="A", cascade=CascadeType.ALL)
private List <B> b;
..
}
@Entity
class B {
@ManyToMany
@JoinTable (
name="A_B",
joinColumns = {@JoinColumn(name="A_ID")},
inverseJoinColumns = {@JoinColumn(name="B_ID")}
)
private List<A> a;
..
}
Questo presuppone una tabella di join denominata A_B con le colonne A_ID e B_ID.
Poiché la relazione è bidirezionale così come l'applicazione si aggiorna da un lato della relazione, anche l'altro lato dovrebbe essere aggiornato, ed essere sincronizzato. In JPA, come in Java in generale è il responsabilità dell'applicazione o del modello a oggetti da mantenere relazioni. Se l'applicazione si aggiunge a un lato di a relazione, quindi deve aggiungere all'altro lato.
Questo può essere risolto tramite l'aggiunta o l'impostazione di metodi nel modello a oggetti che gestiscono entrambi i lati delle relazioni, quindi il codice dell'applicazione non è necessario preoccuparsene. Ci sono due modi per farlo, puoi solo aggiungere il codice di manutenzione della relazione da un lato della relazione e usa il setter solo da un lato (come rendere l'altro lato protetto) o aggiungerlo ad entrambi i lati e assicurarsi eviti un ciclo infinito.
Hai provato ad aggiungere il parametro mappedBy nel campo A in classe B in questo modo
@Entity
class B {
@ManyToMany(cascade=CascadeType.ALL, mappedBy = "b")
private List<A> a;
..
}
Forse c'è un piccolo errore nella risposta di A_M. Secondo me dovrebbe essere:
@Entity class B { @ManyToMany @JoinTable ( name="A_B", joinColumns = {@JoinColumn(name="B_ID")}, inverseJoinColumns = {@JoinColumn(name="A_ID")} ) private List a; .. }