To bardzo częste pytanie, więc odpowiedź opiera się na artykule, który napisałem na swoim blogu.
Jeden za dużo
Relacja jeden do wielu tabel wygląda następująco:

W systemie relacyjnej bazy danych relacja między tabelami jeden do wielu łączy dwie tabele na podstawie Foreign Keykolumny podrzędnej, która odwołuje się Primary Keydo wiersza tabeli nadrzędnej.
Na powyższym diagramie tabeli post_idkolumna w post_commenttabeli ma Foreign Keyzwiązek z kolumną postidentyfikatora tabeli Primary Key:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
Adnotacja @ManyToOne
Najlepszym sposobem odwzorowania relacji jeden do wielu w tabeli jest użycie @ManyToOneadnotacji.
W naszym przypadku encja podrzędna PostCommentmapuje post_idkolumnę klucza obcego za pomocą @ManyToOneadnotacji:
@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;
}
Korzystanie z @OneToManyadnotacji JPA
Tylko dlatego, że masz możliwość korzystania z @OneToManyadnotacji, nie oznacza to, że powinna to być opcja domyślna dla każdego typu „ jeden do wielu” relacji bazie danych. Problem z kolekcjami polega na tym, że możemy ich używać tylko wtedy, gdy liczba rekordów podrzędnych jest raczej ograniczona.
Najlepszym sposobem na zmapowanie @OneToManypowiązania jest poleganie na @ManyToOnestronie przy propagowaniu wszystkich zmian stanu encji:
@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);
}
}
Jednostka nadrzędna Postposiada dwie metody narzędziowe (np. addCommentI removeComment), które są używane do synchronizacji obu stron dwukierunkowego powiązania. Zawsze powinieneś podawać te metody, gdy pracujesz z asocjacją dwukierunkową, ponieważ w przeciwnym razie ryzykujesz bardzo subtelnymi problemami z propagacją stanu .
Należy @OneToManyunikać skojarzenia jednokierunkowego, ponieważ jest mniej wydajne niż użycie @ManyToOnelub @OneToManyskojarzenie dwukierunkowe .
Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @OneToManyrelacji z JPA i Hibernacją, zapoznaj się z tym artykułem .
Jeden na jednego
Relacja jeden do jednego tabeli wygląda następująco:

W systemie relacyjnej bazy danych relacja między tabelami jeden do jednego łączy dwie tabele na podstawie Primary Keykolumny w potomku, która jest również Foreign Keyodwołaniem do Primary Keywiersza tabeli nadrzędnej.
Dlatego możemy powiedzieć, że tabela podrzędna współdzieli Primary Keytabelę nadrzędną.
Na powyższym diagramie tabeli idkolumna w post_detailstabeli ma również Foreign Keyzwiązek z kolumną posttabeli id Primary Key:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
Używanie JPA @OneToOnez @MapsIdadnotacjami
Najlepszym sposobem odwzorowania @OneToOnerelacji jest użycie @MapsId. W ten sposób nie potrzebujesz nawet powiązania dwukierunkowego, ponieważ zawsze możesz pobrać PostDetailsjednostkę za pomocą Postidentyfikatora jednostki.
Mapowanie wygląda następująco:
[code language = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") public class PostDetails {
@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
} [/kod]
W ten sposób idwłaściwość służy zarówno jako klucz podstawowy, jak i klucz obcy. Zauważysz, że @Idkolumna nie używa już @GeneratedValueadnotacji, ponieważ identyfikator jest wypełniony identyfikatorem postpowiązania.
Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @OneToOnerelacji z JPA i Hibernacją, zapoznaj się z tym artykułem .
Wiele do wielu
Relacja między tabelami wiele do wielu wygląda następująco:

W systemie relacyjnej bazy danych relacja wiele-do-wielu tabel łączy dwie tabele nadrzędne za pośrednictwem tabeli podrzędnej, która zawiera dwie Foreign Keykolumny odwołujące się do Primary Keykolumn dwóch tabel nadrzędnych.
Na powyższym schemacie tabeli post_idkolumna w post_tagtabeli ma również Foreign Keyzwiązek z kolumną postid tabeli Primary Key:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
A tag_idkolumna w post_tagtabeli ma Foreign Keyzwiązek z kolumną tagidentyfikatora tabeli Primary Key:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
Korzystanie z @ManyToManymapowania JPA
Oto jak można odwzorować many-to-manyrelacje między tabelami za pomocą JPA i 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);
}
}
tagsStowarzyszenie w Postjednostce określa tylko PERSISTi MERGEkaskadowych typy. Jak wyjaśniono w tym artykule , REMOVE zmiana stanu jednostki nie ma żadnego sensu w przypadku @ManyToManypowiązania JPA, ponieważ może spowodować usunięcie łańcucha, które ostatecznie wymazałoby obie strony skojarzenia.
- Jak wyjaśniono w tym artykule , metody narzędziowe dodaj / usuń są obowiązkowe, jeśli używasz skojarzeń dwukierunkowych, aby upewnić się, że obie strony powiązania są zsynchronizowane.
PostJednostka wykorzystuje identyfikator podmiotu, na rzecz równości, ponieważ brakuje żadnego unikalnego klucza biznesowego. Jak wyjaśniono w tym artykule , możesz użyć identyfikatora jednostki dla równości, o ile upewnisz się, że pozostaje on spójny we wszystkich przejściach stanu jednostki .
TagJednostka posiada unikalny klucz przedsiębiorcy, który jest oznaczony Hibernate specyficznych @NaturalIdadnotacji. W takim przypadku unikalny klucz biznesowy jest najlepszym kandydatem do kontroli równości .
mappedByAtrybutem postszrzeszania się w Tagznakach podmiot, który w tym dwukierunkowej relacji, Postjednostka posiada stowarzyszenie. Jest to potrzebne, ponieważ tylko jedna strona może być właścicielem relacji, a zmiany są propagowane do bazy danych tylko z tej konkretnej strony.
- Powinien
Setbyć preferowany, ponieważ używanie Listwith @ManyToManyjest mniej wydajne.
Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @ManyToManyrelacji z JPA i Hibernacją, zapoznaj się z tym artykułem .