java - ejemplo - ¿Cuál es la diferencia entre @JoinColumn y mappedBy al usar una asociación JPA @OneToMany?
one-to-many hibernate ejemplo (5)
Como expliqué en este artículo , si usa la anotación @JoinColumn
con @JoinColumn
, entonces tiene una asociación unidireccional.
Si usa @OneToMany
con el mappedBy
atributos mappedBy
, tiene una asociación bidireccional, lo que significa que necesita tener una asociación @ManyToOne
en el lado secundario al que mappedBy
referencia mappedBy
.
La asociación unidireccional @OneToMany
no funciona muy bien , por lo que debe evitarla.
Es mejor utilizar el @OneToMany
bidireccional que es más eficiente .
Cuál es la diferencia entre:
@Entity
public class Company {
@OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
@JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
private List<Branch> branches;
...
}
y
@Entity
public class Company {
@OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy = "companyIdRef")
private List<Branch> branches;
...
}
La anotación @JoinColumn
indica que esta entidad es la propietaria 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 en este lado es la inversa de relación, y el propietario reside en la "otra" entidad. Esto también significa que puede acceder a la otra tabla desde la clase que ha anotado con "mappedBy" (relación totalmente bidireccional).
En particular, para el código en la pregunta, las anotaciones correctas se verían así:
@Entity
public class Company {
@OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
private List<Branch> branches;
}
@Entity
public class Branch {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "companyId")
private Company company;
}
La anotación mappedBy idealmente debería usarse siempre en el lado principal (clase de empresa) de la relación bidireccional, en este caso debería estar en la clase de empresa que apunta a la variable miembro ''company'' de la clase secundaria (clase de rama)
La anotación @JoinColumn se usa para especificar una columna asignada para unirse a una asociación de entidad, esta anotación se puede usar en cualquier clase (principal o secundaria), pero lo ideal sería que se use solo en un lado (ya sea en la clase primaria o en la clase secundaria) en ambos) aquí, en este caso, lo usé en el lado Infantil (clase de rama) de la relación bidireccional que indica la clave externa en la clase de rama.
A continuación se muestra el ejemplo de trabajo:
clase padre, empresa
@Entity
public class Company {
private int companyId;
private String companyName;
private List<Branch> branches;
@Id
@GeneratedValue
@Column(name="COMPANY_ID")
public int getCompanyId() {
return companyId;
}
public void setCompanyId(int companyId) {
this.companyId = companyId;
}
@Column(name="COMPANY_NAME")
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
public List<Branch> getBranches() {
return branches;
}
public void setBranches(List<Branch> branches) {
this.branches = branches;
}
}
clase infantil, rama
@Entity
public class Branch {
private int branchId;
private String branchName;
private Company company;
@Id
@GeneratedValue
@Column(name="BRANCH_ID")
public int getBranchId() {
return branchId;
}
public void setBranchId(int branchId) {
this.branchId = branchId;
}
@Column(name="BRANCH_NAME")
public String getBranchName() {
return branchName;
}
public void setBranchName(String branchName) {
this.branchName = branchName;
}
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="COMPANY_ID")
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
Solo me gustaría agregar que @JoinColumn
no siempre tiene que estar relacionado con la ubicación de la información física como sugiere this respuesta. Puede combinar @JoinColumn
con @OneToMany
incluso si la tabla principal no tiene datos de tabla que apunten a la tabla secundaria.
Cómo definir la relación unidireccional de OneToMany en JPA
OneToMany unidireccional, No Invertir ManyToOne, No Join Table
Aunque parece que solo está disponible en JPA 2.x+
. Es útil para situaciones en las que desea que la clase secundaria solo contenga el ID del padre, no una referencia completa.
@JoinColumn
podría usarse en ambos lados de la relación. La pregunta era sobre el uso de @JoinColumn
en el lado de @OneToMany
(caso raro). Y el punto aquí está en la duplicación de información física (nombre de columna) junto con una consulta SQL no optimizada que producirá algunas declaraciones de ACTUALIZACIÓN adicionales .
Según la documentation :
Dado que muchos a uno son (casi) siempre el lado 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() {
...
}
La tropa tiene una relación bidireccional de uno a muchos con el soldado a través de la propiedad de la tropa. No tiene que (no debe) definir ninguna asignación física en el lado mappedBy.
Para asignar un bidireccional a muchos, con el lado uno a muchos como el lado propietario , debe eliminar el elemento mappedBy y establecer el valor de muchos en uno @JoinColumn como insertable y actualizable a falso. Esta solución no está optimizada y producirá algunas declaraciones de ACTUALIZACIÓN adicionales.
@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() {
...
}