Esta es una pregunta muy común, por lo que esta respuesta se basa en este artículo que escribí en mi blog.
Uno a muchos
La relación de la tabla uno a muchos se ve de la siguiente manera:
En un sistema de base de datos relacional, una relación de tabla de uno a muchos vincula dos tablas basadas en una Foreign Key
columna en el elemento secundario que hace referencia a la Primary Key
fila de la tabla principal.
En el diagrama de la tabla anterior, la post_id
columna en la post_comment
tabla tiene una Foreign Key
relación con la columna de post
id de la tabla Primary Key
:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
Anotación @ManyToOne
La mejor manera de mapear la relación de la tabla uno a muchos es usar la @ManyToOne
anotación.
En nuestro caso, la entidad secundaria PostComment
asigna la post_id
columna Clave externa utilizando la @ManyToOne
anotación:
@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;
}
Usando la @OneToMany
anotación JPA
El hecho de que tenga la opción de usar la @OneToMany
anotación, no significa que esta debería ser la opción predeterminada para cada relación de base de datos de uno a muchos . El problema con las colecciones es que solo podemos usarlas cuando el número de registros secundarios es bastante limitado.
La mejor manera de mapear una @OneToMany
asociación es confiar en el @ManyToOne
lado para propagar todos los cambios de estado de la entidad:
@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);
}
}
La entidad principal Post
, presenta dos métodos de utilidad (por ejemplo, addComment
y removeComment
) que se utilizan para sincronizar ambos lados de la asociación bidireccional. Siempre debe proporcionar estos métodos siempre que trabaje con una asociación bidireccional ya que, de lo contrario, corre el riesgo de tener problemas de propagación de estado muy sutiles .
La @OneToMany
asociación unidireccional debe evitarse ya que es menos eficiente que usar @ManyToOne
o la @OneToMany
asociación bidireccional .
Para obtener más detalles sobre la mejor manera de mapear la @OneToMany
relación con JPA e Hibernate, consulte este artículo .
Doce y cincuenta y nueve de la noche
La relación de la tabla uno a uno se ve de la siguiente manera:
En un sistema de base de datos relacional, una relación de tabla uno a uno vincula dos tablas basadas en una Primary Key
columna en el elemento secundario que también hace Foreign Key
referencia a la Primary Key
fila de la tabla principal.
Por lo tanto, podemos decir que la tabla secundaria comparte Primary Key
con la tabla primaria.
En el diagrama de la tabla anterior, la id
columna de la post_details
tabla también tiene una Foreign Key
relación con la columna de la post
tabla id
Primary Key
:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
Usando el JPA @OneToOne
con @MapsId
anotaciones
La mejor manera de mapear una @OneToOne
relación es usarla @MapsId
. De esta manera, ni siquiera necesita una asociación bidireccional, ya que siempre puede buscar la PostDetails
entidad utilizando el Post
identificador de entidad.
El mapeo se ve así:
[code language = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") clase pública 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
} [/ código]
De esta manera, la id
propiedad sirve como clave principal y clave externa. Notará que la @Id
columna ya no usa una @GeneratedValue
anotación ya que el identificador se llena con el identificador de la post
asociación.
Para obtener más detalles sobre la mejor manera de mapear la @OneToOne
relación con JPA e Hibernate, consulte este artículo .
Muchos a muchos
La relación de tabla de muchos a muchos tiene el siguiente aspecto:
En un sistema de base de datos relacional, una relación de tabla de muchos a muchos vincula dos tablas principales a través de una tabla secundaria que contiene dos Foreign Key
columnas que hacen referencia a las Primary Key
columnas de las dos tablas principales.
En el diagrama de la tabla anterior, la post_id
columna de la post_tag
tabla también tiene una Foreign Key
relación con la columna de post
ID de la tabla Primary Key
:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
Y, la tag_id
columna en la post_tag
tabla tiene una Foreign Key
relación con la columna de tag
id de la tabla Primary Key
:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
Usando el @ManyToMany
mapeo JPA
Así es como puede mapear la many-to-many
relación de la tabla con JPA e 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);
}
}
- La
tags
asociación en la Post
entidad solo define los tipos PERSIST
y MERGE
cascada. Como se explica en este artículo , la REMOVE
transición del estado de la entidad no tiene ningún sentido para una @ManyToMany
asociación JPA, ya que podría desencadenar una eliminación de la cadena que finalmente eliminaría ambos lados de la asociación.
- Como se explica en este artículo , los métodos de utilidad de agregar / quitar son obligatorios si usa asociaciones bidireccionales para asegurarse de que ambos lados de la asociación estén sincronizados.
- La
Post
entidad utiliza el identificador de entidad para la igualdad, ya que carece de una clave comercial única. Como se explica en este artículo , puede usar el identificador de entidad para la igualdad siempre que se asegure de que sea coherente en todas las transiciones de estado de entidad .
- La
Tag
entidad tiene una clave comercial única que está marcada con la @NaturalId
anotación específica de Hibernate . Cuando ese es el caso, la clave comercial única es el mejor candidato para los controles de igualdad .
- El
mappedBy
atributo de la posts
asociación en la Tag
entidad marca que, en esta relación bidireccional, la Post
entidad posee la asociación. Esto es necesario ya que solo un lado puede ser dueño de una relación, y los cambios solo se propagan a la base de datos desde este lado en particular.
- El
Set
es preferible, ya que usar un List
con @ManyToMany
es menos eficiente.
Para obtener más detalles sobre la mejor manera de mapear la @ManyToMany
relación con JPA e Hibernate, consulte este artículo .