¿En qué caso utiliza la @JoinTable
anotación JPA ?
¿En qué caso utiliza la @JoinTable
anotación JPA ?
Respuestas:
EDITAR 2017-04-29 : Como señalaron algunos de los comentaristas, el JoinTable
ejemplo no necesita el mappedBy
atributo de anotación. De hecho, las versiones recientes de Hibernate se niegan a iniciarse imprimiendo el siguiente error:
org.hibernate.AnnotationException:
Associations marked as mappedBy must not define database mappings
like @JoinTable or @JoinColumn
Supongamos que tiene una entidad llamada Project
y otra entidad nombrada Task
y cada proyecto puede tener muchas tareas.
Puede diseñar el esquema de la base de datos para este escenario de dos maneras.
La primera solución es crear una tabla llamada Project
y otra tabla llamada Task
y agregar una columna de clave externa a la tabla de tareas llamada project_id
:
Project Task
------- ----
id id
name name
project_id
De esta manera, será posible determinar el proyecto para cada fila en la tabla de tareas. Si usa este enfoque, en sus clases de entidad no necesitará una tabla de unión:
@Entity
public class Project {
@OneToMany(mappedBy = "project")
private Collection<Task> tasks;
}
@Entity
public class Task {
@ManyToOne
private Project project;
}
La otra solución es usar una tercera tabla, por ejemplo Project_Tasks
, y almacenar la relación entre proyectos y tareas en esa tabla:
Project Task Project_Tasks
------- ---- -------------
id id project_id
name name task_id
La Project_Tasks
tabla se llama "Unir tabla". Para implementar esta segunda solución en JPA, debe usar la @JoinTable
anotación. Por ejemplo, para implementar una asociación unidireccional de uno a muchos, podemos definir nuestras entidades como tales:
Project
entidad:
@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
entidad:
@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;
}
}
Esto creará la siguiente estructura de base de datos:
La @JoinTable
anotación también le permite personalizar varios aspectos de la tabla de unión. Por ejemplo, si hubiéramos anotado la tasks
propiedad de esta manera:
@JoinTable(
name = "MY_JT",
joinColumns = @JoinColumn(
name = "PROJ_ID",
referencedColumnName = "PID"
),
inverseJoinColumns = @JoinColumn(
name = "TASK_ID",
referencedColumnName = "TID"
)
)
@OneToMany
private List<Task> tasks;
La base de datos resultante se habría convertido en:
Finalmente, si desea crear un esquema para una asociación de muchos a muchos, el uso de una tabla de unión es la única solución disponible.
@JoinTable/@JoinColumn
puede ser anotada en el mismo campo con mappedBy
. Así que el ejemplo correcto debería ser mantener el mappedBy
en Project
, y mover el @JoinColumn
a Task.project
(o viceversa)
Project_Tasks
necesita el name
de Task
, así, que se convierte en tres columnas: project_id
, task_id
, task_name
, cómo lograr esto?
Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
También es más limpio de usar @JoinTable
cuando una entidad podría ser el niño en varias relaciones padre / hijo con diferentes tipos de padres. Para seguir con el ejemplo de Behrang, imagine que una Tarea puede ser hija de Proyecto, Persona, Departamento, Estudio y Proceso.
¿Debe la task
tabla tener 5 nullable
campos de clave externa? Yo creo que no...
Es la única solución para mapear una asociación ManyToMany: necesita una tabla de unión entre las dos tablas de entidades para mapear la asociación.
También se usa para asociaciones OneToMany (generalmente unidireccionales) cuando no desea agregar una clave foránea en la tabla del lado múltiple y así mantenerla independiente del lado uno.
Busque @JoinTable en la documentación de hibernación para obtener explicaciones y ejemplos.
Te permite manejar una relación de Muchos a Muchos. Ejemplo:
Table 1: post
post has following columns
____________________
| ID | DATE |
|_________|_________|
| | |
|_________|_________|
Table 2: user
user has the following columns:
____________________
| ID |NAME |
|_________|_________|
| | |
|_________|_________|
Join Table le permite crear una asignación utilizando:
@JoinTable(
name="USER_POST",
joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))
creará una tabla:
____________________
| USER_ID| POST_ID |
|_________|_________|
| | |
|_________|_________|
@ManyToMany
asociacionesEn la mayoría de los casos, necesitará usar @JoinTable
anotaciones para especificar la asignación de una relación de tabla de varios a varios:
Entonces, suponiendo que tenga las siguientes tablas de base de datos:
En la Post
entidad, mapearía esta relación, así:
@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<>();
La @JoinTable
anotación se utiliza para especificar el nombre de la tabla a través del name
atributo, así como la columna de clave externa que hace referencia a la post
tabla (por ejemplo, joinColumns
) y la columna de clave externa en la post_tag
tabla de enlaces que hace referencia a la Tag
entidad a través del inverseJoinColumns
atributo.
Observe que el atributo en cascada de la
@ManyToMany
anotación se establece enPERSIST
yMERGE
solo porque la conexión en cascadaREMOVE
es una mala idea, ya que la declaración DELETE se emitirá para el otro registro principal,tag
en nuestro caso, no para elpost_tag
registro. Para obtener más detalles sobre este tema, consulte este artículo .
@OneToMany
Asociaciones unidireccionalesLas @OneToMany
asociaciones unidireccionales , que carecen de@JoinColumn
mapeo, se comportan como relaciones de tabla de muchos a muchos, en lugar de uno a muchos.
Entonces, suponiendo que tenga las siguientes asignaciones de entidades:
@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 asumirá el siguiente esquema de base de datos para la asignación de entidad anterior:
Como ya se explicó, el @OneToMany
mapeo unidireccional de JPA se comporta como una asociación de muchos a muchos.
Para personalizar la tabla de enlaces, también puede usar la @JoinTable
anotación:
@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<>();
Y ahora, se llamará a la tabla de enlaces post_comment_ref
y las columnas de clave externa serán post_id
, para la post
tabla y post_comment_id
, para la post_comment
tabla.
Las
@OneToMany
asociaciones unidireccionales no son eficientes, por lo que es mejor usar@OneToMany
asociaciones bidireccionales o solo el@ManyToOne
lateral. Consulte este artículo para obtener más detalles sobre este tema.