Questa è una domanda molto comune, quindi questa risposta si basa su questo articolo che ho scritto sul mio blog.
Uno-a-molti
La relazione della tabella uno-a-molti ha il seguente aspetto:
In un sistema di database relazionale, una relazione da una a molte tabelle collega due tabelle basate su una Foreign Key
colonna nel figlio che fa riferimento alla Primary Key
riga della tabella padre.
Nel diagramma della tabella sopra, la post_id
colonna nella post_comment
tabella ha una Foreign Key
relazione con la colonna post
ID tabella Primary Key
:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
@ManyToOne annotation
Il modo migliore per mappare la relazione della tabella uno-a-molti è utilizzare l' @ManyToOne
annotazione.
Nel nostro caso, l'entità figlio, PostComment
mappa la post_id
colonna Chiave esterna usando l' @ManyToOne
annotazione:
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
}
Utilizzo @OneToMany
dell'annotazione JPA
Solo perché hai la possibilità di utilizzare l' @OneToMany
annotazione, ciò non significa che questa dovrebbe essere l'opzione predefinita per ogni relazione di database uno-a-molti . Il problema con le raccolte è che possiamo usarle solo quando il numero di record figlio è piuttosto limitato.
Il modo migliore per mappare @OneToMany
un'associazione è fare affidamento sul @ManyToOne
lato per propagare tutte le modifiche allo stato dell'entità:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
}
L'entità padre Post
presenta due metodi di utilità (ad es. addComment
E removeComment
) che vengono utilizzati per sincronizzare entrambi i lati dell'associazione bidirezionale. Dovresti sempre fornire questi metodi ogni volta che lavori con un'associazione bidirezionale poiché, altrimenti, rischi di presentare problemi di propagazione dello stato molto sottili .
L' @OneToMany
associazione unidirezionale deve essere evitata in quanto è meno efficiente dell'utilizzo @ManyToOne
o @OneToMany
dell'associazione bidirezionale .
Per ulteriori dettagli sul modo migliore per mappare la @OneToMany
relazione con JPA e Hibernate, consulta questo articolo .
Uno a uno
La relazione della tabella uno a uno ha il seguente aspetto:
In un sistema di database relazionale, una relazione di tabella uno a uno collega due tabelle basate su una Primary Key
colonna nel figlio che fa anche Foreign Key
riferimento alla Primary Key
riga della tabella padre.
Pertanto, possiamo dire che la tabella figlio condivide la Primary Key
con la tabella padre.
Nel diagramma della tabella sopra, la id
colonna nella post_details
tabella ha anche una Foreign Key
relazione con la colonna della post
tabella id
Primary Key
:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
Utilizzo dell'APP @OneToOne
con @MapsId
annotazioni
Il modo migliore per mappare una @OneToOne
relazione è usare @MapsId
. In questo modo, non è nemmeno necessaria un'associazione bidirezionale poiché è sempre possibile recuperare l' PostDetails
entità utilizzando l' Post
identificatore di entità.
La mappatura è simile alla seguente:
[code language = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") PostDetails di classe pubblica {
@Id
private Long id;
@Column(name = "created_on")
private Date createdOn;
@Column(name = "created_by")
private String createdBy;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id")
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
} [/codice]
In questo modo, la id
proprietà funge sia da chiave primaria che da chiave esterna. Noterai che la @Id
colonna non utilizza più @GeneratedValue
un'annotazione poiché l'identificatore è popolato con l'identificatore post
dell'associazione.
Per ulteriori dettagli sul modo migliore per mappare la @OneToOne
relazione con JPA e Hibernate, consulta questo articolo .
Molti-a-molti
La relazione della tabella molti-a si presenta come segue:
In un sistema di database relazionale, una relazione di tabella molti-a-molti collega due tabelle padre tramite una tabella figlio che contiene due Foreign Key
colonne che fanno riferimento alle Primary Key
colonne delle due tabelle padre.
Nel diagramma della tabella sopra, la post_id
colonna nella post_tag
tabella ha anche una Foreign Key
relazione con la colonna post
ID tabella Primary Key
:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
Inoltre, la tag_id
colonna nella post_tag
tabella ha una Foreign Key
relazione con la colonna tag
ID tabella Primary Key
:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
Utilizzo del @ManyToMany
mapping JPA
Ecco come è possibile mappare la many-to-many
relazione della tabella con JPA e Hibernate:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
//Getters and setters ommitted for brevity
public void addTag(Tag tag) {
tags.add(tag);
tag.getPosts().add(this);
}
public void removeTag(Tag tag) {
tags.remove(tag);
tag.getPosts().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Post)) return false;
return id != null && id.equals(((Post) o).getId());
}
@Override
public int hashCode() {
return 31;
}
}
@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String name;
@ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
//Getters and setters ommitted for brevity
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tag tag = (Tag) o;
return Objects.equals(name, tag.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
- L'
tags
associazione Post
nell'entità definisce solo i tipi PERSIST
e in MERGE
cascata. Come spiegato in questo articolo , la REMOVE
transizione dello stato dell'entità non ha alcun senso per un'associazione @ManyToMany
JPA poiché potrebbe innescare una cancellazione della catena che alla fine cancellerebbe entrambi i lati dell'associazione.
- Come spiegato in questo articolo , i metodi di utilità Aggiungi / Rimuovi sono obbligatori se si utilizzano associazioni bidirezionali in modo da poter assicurarsi che entrambi i lati dell'associazione siano sincronizzati.
- L'
Post
entità utilizza l'identificatore di entità per l'uguaglianza poiché non dispone di alcuna chiave aziendale univoca. Come spiegato in questo articolo , è possibile utilizzare l'identificatore di entità per l'uguaglianza purché si assicuri che rimanga coerente in tutte le transizioni di stato dell'entità .
- L'
Tag
entità ha una chiave aziendale univoca contrassegnata con l' @NaturalId
annotazione specifica di Hibernate . In tal caso, la chiave aziendale unica è il miglior candidato per i controlli di uguaglianza .
- L'
mappedBy
attributo posts
dell'associazione Tag
nell'entità indica che, in questa relazione bidirezionale, l' Post
entità possiede l'associazione. Ciò è necessario poiché solo una parte può possedere una relazione e le modifiche vengono propagate al database solo da questa parte specifica.
- È
Set
preferibile, poiché l'uso di List
con @ManyToMany
è meno efficiente.
Per ulteriori dettagli sul modo migliore per mappare la @ManyToMany
relazione con JPA e Hibernate, consulta questo articolo .