W jakim przypadku używasz @JoinTable
adnotacji JPA ?
W jakim przypadku używasz @JoinTable
adnotacji JPA ?
Odpowiedzi:
EDYCJA 2017-04-29 : Jak zauważyli niektórzy komentatorzy, JoinTable
przykład nie potrzebuje mappedBy
atrybutu adnotacji. W rzeczywistości najnowsze wersje Hibernacji odmawiają uruchomienia, wyświetlając następujący błąd:
org.hibernate.AnnotationException:
Associations marked as mappedBy must not define database mappings
like @JoinTable or @JoinColumn
Załóżmy, że masz jednostkę o nazwie Project
i inną o nazwie, Task
a każdy projekt może mieć wiele zadań.
Schemat bazy danych można zaprojektować dla tego scenariusza na dwa sposoby.
Pierwszym rozwiązaniem jest utworzenie tabeli o nazwie Project
i innej tabeli o nazwie Task
i dodanie kolumny klucza obcego do tabeli zadań o nazwie project_id
:
Project Task
------- ----
id id
name name
project_id
W ten sposób będzie można określić projekt dla każdego wiersza w tabeli zadań. Jeśli używasz tego podejścia, w klasach encji nie będziesz potrzebować tabeli łączenia:
@Entity
public class Project {
@OneToMany(mappedBy = "project")
private Collection<Task> tasks;
}
@Entity
public class Task {
@ManyToOne
private Project project;
}
Innym rozwiązaniem jest użycie np. Trzeciej tabeli Project_Tasks
i zapisanie relacji między projektami i zadaniami w tej tabeli:
Project Task Project_Tasks
------- ---- -------------
id id project_id
name name task_id
Project_Tasks
Tabela nazywa się „Join Table”. Aby wdrożyć to drugie rozwiązanie w JPA, musisz użyć @JoinTable
adnotacji. Na przykład, aby zaimplementować jednokierunkowe powiązanie jeden do wielu, możemy zdefiniować nasze jednostki jako takie:
Project
jednostka:
@Entity
public class Project {
@Id
@GeneratedValue
private Long pid;
private String name;
@JoinTable
@OneToMany
private List<Task> tasks;
public Long getPid() {
return pid;
}
public void setPid(Long pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Task> getTasks() {
return tasks;
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
}
}
Task
jednostka:
@Entity
public class Task {
@Id
@GeneratedValue
private Long tid;
private String name;
public Long getTid() {
return tid;
}
public void setTid(Long tid) {
this.tid = tid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Spowoduje to utworzenie następującej struktury bazy danych:
@JoinTable
Adnotacji pozwala również dostosować różne aspekty tabeli sprzężenia. Na przykład, gdybyśmy opisali tasks
właściwość w ten sposób:
@JoinTable(
name = "MY_JT",
joinColumns = @JoinColumn(
name = "PROJ_ID",
referencedColumnName = "PID"
),
inverseJoinColumns = @JoinColumn(
name = "TASK_ID",
referencedColumnName = "TID"
)
)
@OneToMany
private List<Task> tasks;
Powstała baza danych stałaby się:
Wreszcie, jeśli chcesz utworzyć schemat dla asocjacji wiele do wielu, użycie tabeli łączenia jest jedynym dostępnym rozwiązaniem.
@JoinTable/@JoinColumn
w tym samym polu można umieścić adnotację mappedBy
. Tak więc poprawnym przykładem powinno być trzymanie mappedBy
w Project
środku i przejście @JoinColumn
do Task.project
(lub odwrotnie)
Project_Tasks
potrzebuje name
od Task
a także, co staje się trzy kolumny: project_id
, task_id
, task_name
, jak to osiągnąć?
Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
Jest również bardziej przejrzysty w użyciu, @JoinTable
gdy jednostka może być dzieckiem w kilku relacjach rodzic / dziecko z różnymi typami rodziców. Kontynuując przykład Behranga, wyobraź sobie, że zadanie może być dzieckiem projektu, osoby, działu, badania i procesu.
Czy task
tabela powinna mieć 5 nullable
pól kluczy obcych? Myślę, że nie...
Jest to jedyne rozwiązanie umożliwiające mapowanie asocjacji ManyToMany: do mapowania powiązania potrzebna jest tabela łączenia między dwiema tabelami encji.
Jest również używany do asocjacji OneToMany (zwykle jednokierunkowych), gdy nie chcesz dodawać klucza obcego w tabeli wielu stron, a tym samym utrzymywać go niezależnie od jednej strony.
Wyszukaj @JoinTable w dokumentacji hibernacji, aby znaleźć wyjaśnienia i przykłady.
To pozwala ci radzić sobie w relacjach Wiele do wielu. Przykład:
Table 1: post
post has following columns
____________________
| ID | DATE |
|_________|_________|
| | |
|_________|_________|
Table 2: user
user has the following columns:
____________________
| ID |NAME |
|_________|_________|
| | |
|_________|_________|
Join Table umożliwia tworzenie mapowań przy użyciu:
@JoinTable(
name="USER_POST",
joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))
utworzy tabelę:
____________________
| USER_ID| POST_ID |
|_________|_________|
| | |
|_________|_________|
@ManyToMany
wspomnieniaNajczęściej będziesz musiał użyć @JoinTable
adnotacji, aby określić mapowanie relacji wiele do wielu tabel:
Więc zakładając, że masz następujące tabele bazy danych:
W Post
encji można zmapować tę relację w następujący sposób:
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(
name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();
@JoinTable
Adnotacja jest używana do określenia nazwy tabel za pomocą name
atrybutu, a także kolumny klucza obcego która odwołuje się do post
tabeli (na przykład joinColumns
), a kolumna klucz obcy w post_tag
tabeli połączeń, która odwołuje się Tag
podmiot za pośrednictwem inverseJoinColumns
atrybutu.
Zauważ, że atrybut kaskady
@ManyToMany
adnotacji jest ustawiony naPERSIST
iMERGE
tylko dlatego, że kaskadowanieREMOVE
jest złym pomysłem, ponieważ instrukcja DELETE zostanie wydana dla innego rekordu nadrzędnego,tag
w naszym przypadku, a nie dlapost_tag
rekordu. Więcej informacji na ten temat znajdziesz w tym artykule .
@OneToMany
Asocjacje jednokierunkowe@OneToMany
Skojarzenia jednokierunkowe , którym brakuje @JoinColumn
odwzorowania, zachowują się jak relacje między tabelami wiele do wielu, a nie jeden do wielu.
Więc zakładając, że masz następujące mapowania encji:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
}
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
//Constructors, getters and setters removed for brevity
}
Hibernate przyjmie następujący schemat bazy danych dla powyższego mapowania encji:
Jak już wyjaśniono, jednokierunkowe @OneToMany
mapowanie JPA zachowuje się jak asocjacja wiele do wielu.
Aby dostosować tabelę linków, możesz również użyć @JoinTable
adnotacji:
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
@JoinTable(
name = "post_comment_ref",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();
A teraz zostanie wywołana tabela linków, post_comment_ref
a kolumny klucza obcego będą post_id
dla post
tabeli i post_comment_id
dla post_comment
tabeli.
@OneToMany
Powiązania jednokierunkowe nie są wydajne, więc lepiej jest używać@OneToMany
asocjacji dwukierunkowych lub tylko@ManyToOne
bocznych. Zapoznaj się z tym artykułem, aby uzyskać więcej informacji na ten temat.