Solución:
La anotación @JoinColumn
indica que esta entidad es la dueño de la relación (es decir: la tabla correspondiente tiene una columna con una clave externa a la tabla referenciada), mientras que el atributo mappedBy
indica que la entidad de este lado es la inversa de la relación y el propietario reside en la “otra” entidad. Esto también significa que puede acceder a la otra tabla de la clase que ha anotado con “mappedBy” (relación completamente bidireccional).
En particular, para el código de la pregunta, las anotaciones correctas se verían así:
@Entity
public class Company {
@OneToMany(mappedBy = "company",
orphanRemoval = true,
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Branch> branches;
}
@Entity
public class Branch {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "companyId")
private Company company;
}
@JoinColumn
podría usarse en ambos lados de la relación. La pregunta era sobre usar @JoinColumn
sobre el @OneToMany
lado (caso raro). Y el punto aquí está en duplicación de información física (nombre de la columna) junto con consulta SQL no optimizada que producirá algunos UPDATE
declaraciones.
Según documentación:
Ya que muchos a uno son (casi) siempre el lado del propietario de una relación bidireccional en la especificación JPA, la asociación de uno a muchos está anotada por @OneToMany(mappedBy=...)
@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Troop
tiene una relación bidireccional de uno a muchos con Soldier
a través de la propiedad de la tropa. No tiene que (no debe) definir ningún mapeo físico en el mappedBy
lado.
Para mapear un bidireccional uno a muchos, con el lado uno a muchos como el lado propietario, tienes que quitar el mappedBy
elemento y establezca muchos en uno @JoinColumn
como insertable
y updatable
a falso. Esta solución no está optimizada y producirá algunos UPDATE
declaraciones.
@Entity
public class Troop {
@OneToMany
@JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
Asociación unidireccional de uno a varios
Si usa el @OneToMany
anotación con @JoinColumn
, entonces tienes una asociación unidireccional, como la que existe entre el padre Post
entidad y el niño PostComment
en el siguiente diagrama:
Cuando se usa una asociación unidireccional de uno a muchos, solo el lado principal mapea la asociación.
En este ejemplo, solo el Post
entidad definirá un @OneToMany
asociación con el niño PostComment
entidad:
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();
Asociación bidireccional de uno a varios
Si usa el @OneToMany
con el mappedBy
conjunto de atributos, tiene una asociación bidireccional. En nuestro caso, tanto el Post
entidad tiene una colección de PostComment
entidades secundarias, y la secundaria PostComment
la entidad tiene una referencia a la matriz Post
entidad, como se ilustra en el siguiente diagrama:
En el PostComment
entidad, la post
la propiedad de la entidad se asigna de la siguiente manera:
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
La razón por la que establecimos explícitamente el
fetch
atribuir aFetchType.LAZY
es porque, por defecto, todos@ManyToOne
y@OneToOne
las asociaciones se obtienen con entusiasmo, lo que puede provocar problemas de consulta N + 1.
En el Post
entidad, la comments
La asociación se asigna de la siguiente manera:
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
los mappedBy
atributo del @OneToMany
la anotación hace referencia al post
propiedad en el niño PostComment
entidad, y, de esta manera, Hibernate sabe que la asociación bidireccional está controlada por el @ManyToOne
side, que se encarga de administrar el valor de la columna de clave externa en la que se basa esta relación de tabla.
Para una asociación bidireccional, también necesita tener dos métodos de utilidad, como addChild
y removeChild
:
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
Estos dos métodos garantizan que ambos lados de la asociación bidireccional estén sincronizados. Sin sincronizar ambos extremos, Hibernate no garantiza que los cambios de estado de la asociación se propaguen a la base de datos.
¿Cuál elegir?
El unidireccional @OneToMany
La asociación no funciona muy bien, por lo que debe evitarla.
Es mejor usar el bidireccional @OneToMany
que es más eficiente.