¿En qué caso utiliza la anotación JPA @JoinTable?

Respuestas:

350

EDITAR 2017-04-29 : Como señalaron algunos de los comentaristas, el JoinTableejemplo no necesita el mappedByatributo 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 Projecty otra entidad nombrada Tasky 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 Projecty otra tabla llamada Tasky 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_Taskstabla se llama "Unir tabla". Para implementar esta segunda solución en JPA, debe usar la @JoinTableanotació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:

Diagrama ER 1

La @JoinTableanotación también le permite personalizar varios aspectos de la tabla de unión. Por ejemplo, si hubiéramos anotado la taskspropiedad 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:

Diagrama ER 2

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.

Behrang Saeedzadeh
fuente
1
usando el primer enfoque, llevo mi proyecto con mis tareas y cada tarea llena con el proyecto principal antes de la fusión y funciona, pero todas mis entradas se duplican en función del número de mis tareas. Un proyecto con dos tareas se guarda dos veces en mi base de datos. Por qué ?
MaikoID
ACTUALIZACIÓN No hay entradas duplicadas en mi base de datos, la hibernación se está seleccionando con la combinación externa izquierda y no sé por qué ...
MaikoID
2
Creo que @JoinTable/@JoinColumnpuede ser anotada en el mismo campo con mappedBy. Así que el ejemplo correcto debería ser mantener el mappedByen Project, y mover el @JoinColumna Task.project (o viceversa)
Adrian Shum
2
¡Agradable! Pero tengo una pregunta más: si la tabla de unión Project_Tasksnecesita el namede Task, así, que se convierte en tres columnas: project_id, task_id, task_name, cómo lograr esto?
macemers
55
Creo que no debería haber mapeado Por su segundo ejemplo de uso para evitar este errorCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m
14

También es más limpio de usar @JoinTablecuando 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 tasktabla tener 5 nullablecampos de clave externa? Yo creo que no...

HDave
fuente
14

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.

JB Nizet
fuente
4

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 |
|_________|_________|
|         |         |
|_________|_________|
slal
fuente
1
Pregunta: ¿y si ya tengo esta tabla adicional? JoinTable no sobrescribirá el existente, ¿verdad?
TheWandererr
@ TheWandererr, ¿descubrió la respuesta a su pregunta? Ya tengo una mesa de unión
pide el
En mi caso, está creando una columna redundante en la tabla lateral propietaria. por ej. POST_ID en POST. ¿Puedes sugerir por qué está sucediendo?
SPS
0

@ManyToMany asociaciones

En la mayoría de los casos, necesitará usar @JoinTableanotaciones para especificar la asignación de una relación de tabla de varios a varios:

  • el nombre de la tabla de enlaces y
  • las dos columnas de clave externa

Entonces, suponiendo que tenga las siguientes tablas de base de datos:

Relación de tabla de muchos a muchos

En la Postentidad, 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 @JoinTableanotación se utiliza para especificar el nombre de la tabla a través del nameatributo, así como la columna de clave externa que hace referencia a la posttabla (por ejemplo, joinColumns) y la columna de clave externa en la post_tagtabla de enlaces que hace referencia a la Tagentidad a través del inverseJoinColumnsatributo.

Observe que el atributo en cascada de la @ManyToManyanotación se establece en PERSISTy MERGEsolo porque la conexión en cascada REMOVEes una mala idea, ya que la declaración DELETE se emitirá para el otro registro principal, tagen nuestro caso, no para el post_tagregistro. Para obtener más detalles sobre este tema, consulte este artículo .

@OneToManyAsociaciones unidireccionales

Las @OneToManyasociaciones 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:

Tablas de bases de datos de asociación unidireccionales <code> @OneToMany </code> JPA

Como ya se explicó, el @OneToManymapeo unidireccional de JPA se comporta como una asociación de muchos a muchos.

Para personalizar la tabla de enlaces, también puede usar la @JoinTableanotació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_refy las columnas de clave externa serán post_id, para la posttabla y post_comment_id, para la post_commenttabla.

Las @OneToManyasociaciones unidireccionales no son eficientes, por lo que es mejor usar @OneToManyasociaciones bidireccionales o solo el @ManyToOnelateral. Consulte este artículo para obtener más detalles sobre este tema.

Vlad Mihalcea
fuente