Hibernate @OneToMany mappatura delle relazioni
-
27-10-2019 - |
Domanda
Sto cercando di progettare un qualche tipo di relazione utente-utente, ad esempio "l'utente A segue l'utente B" e "L'utente A vuole essere amico dell'utente B".
Ho una classe User e il modo in cui è progettata è simile a questa:
@Entity
public class User{
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
List<User> followers;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
List<User> following;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
List<User> friendRequests;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
List<User> requesting;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
List<User> friends;
}
Sto riscontrando due problemi:
- Hibernate mi sta dando il problema impossibile recuperare più bagagli contemporaneamente
- Ho cercato online, la gente diceva di rimuovere FetchType.EAGER o di cambiarlo in Imposta invece di Elenco, ma il risultato è stato Il campo non ha un valore predefinito
Ho la sensazione che la relazione non sia definita correttamente e inoltre dovrei vedere più tabelle, perché in questo momento vedo solo la tabella Utente e la tabella Utente_Utente.
Aggiorna
Quanto segue crea 3 tabelle, amici, follower e richiedenti. È in qualche modo ottimizzato rispetto a 5 tabelle? E c'è qualche vantaggio in questo rispetto a quanto suggerito dal signor J4mes?
@ManyToMany
@JoinTable(name = "followers", joinColumns = @JoinColumn(name = "followerId"), inverseJoinColumns = @JoinColumn(name = "userId"))
private List<User> followers;
@ManyToMany
@JoinTable(name = "followers", joinColumns = @JoinColumn(name = "userId"), inverseJoinColumns = @JoinColumn(name = "followerId"))
private List<User> following;
@ManyToMany
@JoinTable(name = "friends", joinColumns = @JoinColumn(name = "userId"), inverseJoinColumns = @JoinColumn(name = "friendId"))
private List<User> friends;
@ManyToMany
@JoinTable(name = "requesters", joinColumns = @JoinColumn(name = "requesterId"), inverseJoinColumns = @JoinColumn(name = "userId"))
private List<User> friendRequests;
@ManyToMany
@JoinTable(name = "requesters", joinColumns = @JoinColumn(name = "userId"), inverseJoinColumns = @JoinColumn(name = "requesterId"))
private List<User> requesting;
Soluzione
Prima di tutto, per implementare la tua funzione, dovresti usare @ManyToMany
invece di @OneToMany
.Dovrebbe assomigliare a questo:
@Entity
public class User implements Serializable {
@ManyToMany(mappedBy="followers")
@JoinTable(name="followingTable")
private Set<User> following;
@ManyToMany
@JoinTable(name="followerTable")
private Set<User> followers;
@ManyToMany(mappedBy="friendRequests")
@JoinTable(name="requestingTable")
private Set<User> requesting;
@ManyToMany
@JoinTable(name="friendRequestTable")
private Set<User> friendRequests;
@ManyToMany
private Set<User> friends;
}
Le tue relazioni mi sembrano bidirezionali.Se utilizzi @OneToMany
, significa che C has 2 followers A and B
= A and B only follows C
.Tuttavia, il fatto è che una persona può seguire molte persone e una persona può essere seguita da molte persone.In altre parole, A and B can also follow D
.
Inoltre, non dovresti assolutamente usare cascadeType.ALL
.Questa politica a cascata significa che se un utente elimina il suo account e tu elimini la voce corrispondente nel database, anche tutti i suoi amici, ecc.
Altri suggerimenti
Hibernate genererà solo una tabella per l'entità Utente e presumo una tabella di riferimento incrociato per la relazione Utente-Utente.Una soluzione alternativa per avere tabelle diverse potrebbe essere l'impostazione di entità diverse per le diverse relazioni, ad esempio
Entità utente
@OneToMany
List<Follower> followers;
Entità follower
@Entity
class Follower
...
@ManyToOne
User user;
@Id
@Generated
int id;
Per quanto riguarda il recupero EAGER, potresti effettivamente volere che tutti siano PIGRO, poiché a seconda di come è impostato il tuo database, tutti quei caricamenti desiderosi potrebbero essere piuttosto costosi.Solo quando l'utente vuole caricare gli utenti follower, vorrei recuperarli.
Separare User
da Friends
e provare a creare una tabella con almeno 2 colonne che associ il User_ID
a tutto il suo Friends_IDs
che è fondamentalmente un riferimento a User_ID
Inoltre, EAGER
caricherà TUTTI i dati non appena lo chiami per la prima volta.Significa che caricherà tutti gli utenti e i loro amici.Il caricamento degli amici dovrebbe essere LAZY
, ovvero caricato solo quando ne hai bisogno.