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 Key
kolumny podrzędnej, która odwołuje się Primary Key
do wiersza tabeli nadrzędnej.
Na powyższym diagramie tabeli post_id
kolumna w post_comment
tabeli ma Foreign Key
związek z kolumną post
identyfikatora 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 @ManyToOne
adnotacji.
W naszym przypadku encja podrzędna PostComment
mapuje post_id
kolumnę klucza obcego za pomocą @ManyToOne
adnotacji:
@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 @OneToMany
adnotacji JPA
Tylko dlatego, że masz możliwość korzystania z @OneToMany
adnotacji, 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 @OneToMany
powiązania jest poleganie na @ManyToOne
stronie 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 Post
posiada dwie metody narzędziowe (np. addComment
I 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 @OneToMany
unikać skojarzenia jednokierunkowego, ponieważ jest mniej wydajne niż użycie @ManyToOne
lub @OneToMany
skojarzenie dwukierunkowe .
Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @OneToMany
relacji 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 Key
kolumny w potomku, która jest również Foreign Key
odwołaniem do Primary Key
wiersza tabeli nadrzędnej.
Dlatego możemy powiedzieć, że tabela podrzędna współdzieli Primary Key
tabelę nadrzędną.
Na powyższym diagramie tabeli id
kolumna w post_details
tabeli ma również Foreign Key
związek z kolumną post
tabeli id
Primary Key
:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
Używanie JPA @OneToOne
z @MapsId
adnotacjami
Najlepszym sposobem odwzorowania @OneToOne
relacji jest użycie @MapsId
. W ten sposób nie potrzebujesz nawet powiązania dwukierunkowego, ponieważ zawsze możesz pobrać PostDetails
jednostkę za pomocą Post
identyfikatora 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 id
właściwość służy zarówno jako klucz podstawowy, jak i klucz obcy. Zauważysz, że @Id
kolumna nie używa już @GeneratedValue
adnotacji, ponieważ identyfikator jest wypełniony identyfikatorem post
powiązania.
Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @OneToOne
relacji 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 Key
kolumny odwołujące się do Primary Key
kolumn dwóch tabel nadrzędnych.
Na powyższym schemacie tabeli post_id
kolumna w post_tag
tabeli ma również Foreign Key
związek z kolumną post
id tabeli Primary Key
:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
A tag_id
kolumna w post_tag
tabeli ma Foreign Key
związek z kolumną tag
identyfikatora tabeli Primary Key
:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
Korzystanie z @ManyToMany
mapowania JPA
Oto jak można odwzorować many-to-many
relacje 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);
}
}
tags
Stowarzyszenie w Post
jednostce określa tylko PERSIST
i MERGE
kaskadowych typy. Jak wyjaśniono w tym artykule , REMOVE
zmiana stanu jednostki nie ma żadnego sensu w przypadku @ManyToMany
powią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.
Post
Jednostka 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 .
Tag
Jednostka posiada unikalny klucz przedsiębiorcy, który jest oznaczony Hibernate specyficznych @NaturalId
adnotacji. W takim przypadku unikalny klucz biznesowy jest najlepszym kandydatem do kontroli równości .
mappedBy
Atrybutem posts
zrzeszania się w Tag
znakach podmiot, który w tym dwukierunkowej relacji, Post
jednostka 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
Set
być preferowany, ponieważ używanie List
with @ManyToMany
jest mniej wydajne.
Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @ManyToMany
relacji z JPA i Hibernacją, zapoznaj się z tym artykułem .