In quale caso usi l' @JoinTable
annotazione JPA ?
In quale caso usi l' @JoinTable
annotazione JPA ?
Risposte:
EDIT 2017-04-29 : Come sottolineato da alcuni dei commentatori, l' JoinTable
esempio non ha bisogno mappedBy
dell'attributo annotation. In effetti, le versioni recenti di Hibernate si rifiutano di avviarsi stampando il seguente errore:
org.hibernate.AnnotationException:
Associations marked as mappedBy must not define database mappings
like @JoinTable or @JoinColumn
Facciamo finta di avere un'entità nominata Project
e un'altra entità nominataTask
e che ogni progetto possa avere molte attività.
È possibile progettare lo schema del database per questo scenario in due modi.
La prima soluzione è creare una tabella denominata Project
e un'altra tabella denominata Task
e aggiungere una colonna chiave esterna alla tabella attività denominata project_id
:
Project Task
------- ----
id id
name name
project_id
In questo modo, sarà possibile determinare il progetto per ogni riga nella tabella delle attività. Se usi questo approccio, nelle tue classi di entità non avrai bisogno di una tabella di join:
@Entity
public class Project {
@OneToMany(mappedBy = "project")
private Collection<Task> tasks;
}
@Entity
public class Task {
@ManyToOne
private Project project;
}
L'altra soluzione consiste nell'utilizzare una terza tabella, ad esempio Project_Tasks
, e memorizzare la relazione tra progetti e attività in quella tabella:
Project Task Project_Tasks
------- ---- -------------
id id project_id
name name task_id
La Project_Tasks
tabella si chiama "Unisci tabella". Per implementare questa seconda soluzione in JPA è necessario utilizzare l' @JoinTable
annotazione. Ad esempio, al fine di implementare un'associazione uno-a-molti unidirezionale, possiamo definire le nostre entità in quanto tali:
Project
entità:
@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
entità:
@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;
}
}
Ciò creerà la seguente struttura del database:
L' @JoinTable
annotazione consente inoltre di personalizzare vari aspetti della tabella di join. Ad esempio, avevamo annotato la tasks
proprietà in questo modo:
@JoinTable(
name = "MY_JT",
joinColumns = @JoinColumn(
name = "PROJ_ID",
referencedColumnName = "PID"
),
inverseJoinColumns = @JoinColumn(
name = "TASK_ID",
referencedColumnName = "TID"
)
)
@OneToMany
private List<Task> tasks;
Il database risultante sarebbe diventato:
Infine, se si desidera creare uno schema per un'associazione molti-a-molti, l'utilizzo di una tabella di join è l'unica soluzione disponibile.
@JoinTable/@JoinColumn
possa essere annotato sullo stesso campo con mappedBy
. Quindi l'esempio corretto dovrebbe essere mantenendo l' mappedBy
in Project
, e spostare il @JoinColumn
verso Task.project
(o viceversa)
Project_Tasks
ha bisogno name
di Task
così, che diventa tre colonne: project_id
, task_id
, task_name
, come raggiungere questo obiettivo?
Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
È anche più pulito da usare @JoinTable
quando un'entità potrebbe essere il bambino in diverse relazioni padre / figlio con diversi tipi di genitori. Per dare seguito all'esempio di Behrang, immagina che un'attività possa essere figlia di Progetto, Persona, Dipartimento, Studio e Processo.
La task
tabella dovrebbe avere 5 nullable
campi chiave esterna? Penso di no...
È l'unica soluzione per mappare un'associazione ManyToMany: è necessaria una tabella di join tra le due tabelle entità per mappare l'associazione.
Viene anche utilizzato per le associazioni OneToMany (generalmente unidirezionali) quando non si desidera aggiungere una chiave esterna nella tabella dei molti lati e quindi mantenerla indipendente da un lato.
Cerca @JoinTable nella documentazione di ibernazione per spiegazioni ed esempi.
Ti consente di gestire la relazione Many to Many. Esempio:
Table 1: post
post has following columns
____________________
| ID | DATE |
|_________|_________|
| | |
|_________|_________|
Table 2: user
user has the following columns:
____________________
| ID |NAME |
|_________|_________|
| | |
|_________|_________|
Unisci tabella ti consente di creare una mappatura usando:
@JoinTable(
name="USER_POST",
joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))
creerà una tabella:
____________________
| USER_ID| POST_ID |
|_________|_________|
| | |
|_________|_________|
@ManyToMany
associazioniMolto spesso, sarà necessario utilizzare l' @JoinTable
annotazione per specificare la mappatura di una relazione di tabella molti-a-molti:
Quindi, supponendo di avere le seguenti tabelle del database:
Nella Post
entità, si potrebbe mappare questa relazione, in questo modo:
@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<>();
L' @JoinTable
annotazione viene utilizzata per specificare il nome della tabella tramite l' name
attributo, nonché la colonna Chiave esterna che fa riferimento alla post
tabella (ad es. joinColumns
) E la colonna Chiave esterna nella post_tag
tabella dei collegamenti che fa riferimento Tag
all'entità tramite l' inverseJoinColumns
attributo.
Si noti che l'attributo cascade
@ManyToMany
dell'annotazione è impostato suPERSIST
eMERGE
solo perché il cascadingREMOVE
è una cattiva idea poiché noi l'istruzione DELETE verrà emessa per l'altro record padre,tag
nel nostro caso, non per ilpost_tag
record. Per maggiori dettagli su questo argomento, consulta questo articolo .
@OneToMany
Associazioni unidirezionaliLe @OneToMany
associazioni unidirezionali , che mancano a@JoinColumn
mappatura, si comportano come relazioni da molti a molti piuttosto che da uno a molti.
Quindi, supponendo che tu abbia i seguenti mapping di entità:
@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 assumerà il seguente schema di database per il mapping delle entità sopra riportato:
Come già spiegato, la @OneToMany
mappatura unidirezionale JPA si comporta come un'associazione molti-a-molti.
Per personalizzare la tabella dei collegamenti, puoi anche utilizzare l' @JoinTable
annotazione:
@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<>();
E ora verrà chiamata la tabella dei collegamenti post_comment_ref
e le colonne Chiave esterna saranno post_id
, per la post
tabella e post_comment_id
, per la post_comment
tabella.
Le
@OneToMany
associazioni unidirezionali non sono efficienti, quindi è meglio usare le@OneToMany
associazioni bidirezionali o solo il@ManyToOne
lato. Consulta questo articolo per maggiori dettagli su questo argomento.