web-dev-qa-db-fra.com

Quelle est la différence entre @JoinColumn et mappedBy lors de l'utilisation d'une association JPA @OneToMany

Quelle est la différence entre:

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
    private List<Branch> branches;
    ...
}

et

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy = "companyIdRef")
    private List<Branch> branches;
    ...
}
474
Mykhaylo Adamovych

@JoinColumn pourrait être utilisé des deux côtés de la relation. La question portait sur l’utilisation de @JoinColumn sur @OneToMany. côté (cas rare). Et le point ici est dans la duplication d'informations physiques (nom de colonne) avec requête SQL non optimisée qui produira des instructions UPDATE supplémentaires .

Selon documentation :

Depuis plusieurs à un sont (presque) toujours le côté propriétaire d'un relation bidirectionnelle dans la spécification JPA, l'association un à plusieurs est annotée par @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 entretient une relation bidirectionnelle de un à plusieurs avec Soldier via la propriété de troupe. Vous n'avez pas (ne devez pas) définir de mappage physique du côté mappedBy.

Pour mapper un un vers plusieurs bidirectionnel, avec le côté propriétaire un comme plusieurs , vous devez supprimer l'élément mappedBy et définir le à un @JoinColumn insérable et modifiable à false. Cette solution n'est pas optimisée et produira des instructions UPDATE supplémentaires.

@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() {
    ...
}
207
Mykhaylo Adamovych

L'annotation @JoinColumn indique que cette entité est le propriétaire de la relation (c'est-à-dire que la table correspondante a une colonne avec une clé étrangère à la table référencée), tandis que l'attribut mappedBy indique que l'entité de ce côté est l'inverse de la relation et que le propriétaire réside dans "l'autre" entité. Cela signifie également que vous pouvez accéder à l'autre table à partir de la classe annotée avec "mappedBy" (relation totalement bidirectionnelle).

En particulier, pour le code dans la question, les annotations correctes ressembleraient à ceci:

@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;
}
516
Óscar López

Comme je l'ai expliqué dans cet article , si vous utilisez l'annotation _@OneToMany_ avec _@JoinColumn_, vous disposez alors d'une association unidirectionnelle.

Si vous utilisez le _@OneToMany_ avec le jeu d'attributs mappedBy, vous avez une association bidirectionnelle, ce qui signifie que vous devez avoir une association _@ManyToOne_ du côté enfant référée par le mappedBy.

L’association nidirectionnel _@OneToMany_ ne fonctionne pas très bien , vous devez donc l’éviter.

Il vaut mieux utiliser __ bidirectionnel _@OneToMany_ qui est plus efficace .

42
Vlad Mihalcea

L'annotation mappedBy devrait idéalement toujours être utilisée dans le côté parent (classe Société) de la relation bidirectionnelle, dans ce cas, il devrait être dans la classe Société pointant vers la variable membre 'société' de la classe Enfant. (Classe de branche)

L’annotation @ JoinColumn est utilisée pour spécifier une colonne mappée permettant de rejoindre une association d’entités. Cette annotation peut être utilisée dans n’importe quelle classe (parent ou enfant), mais elle devrait idéalement être utilisée uniquement dans un côté classe parent ou dans la classe Child pas dans les deux) ici dans ce cas je l'ai utilisé dans le côté enfant (classe Branch) de la relation bidirectionnelle indiquant la clé étrangère dans la classe Branch.

ci-dessous est l'exemple de travail:

classe mère, Société

@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;
    }


}

classe enfant, branche

@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;
    }


}
32
Coral

Je voudrais juste ajouter que @JoinColumn ne doit pas toujours être associé à l'emplacement de l'information physique comme this suggère. Vous pouvez combiner @JoinColumn avec @OneToMany même si la table parent n'a pas de données de table pointant vers la table enfant.

Comment définir une relation OneToMany unidirectionnelle dans JPA

OneToMany unidirectionnel, pas d'inverse ManyToOne, pas de table de jointure

Il ne semble être disponible que dans JPA 2.x+ bien que. C'est utile dans les situations où vous voulez que la classe enfant contienne uniquement l'ID du parent, pas une référence complète.

19
Snekse

Je ne suis pas d'accord avec la réponse acceptée ici par Óscar López. Cette réponse est inexacte!

Ce n'est PAS @JoinColumn qui indique que cette entité est le propriétaire de la relation. Au lieu de cela, c'est l'annotation @ManyToOne qui le fait (dans son exemple).

Les annotations de relation telles que @ManyToOne, @OneToMany et @ManyToMany indiquent à JPA/Hibernate to créer un mappage. Par défaut, cela se fait via une table de jointure séparée.


@ JoinColumn

Le but de @JoinColumn est de créer une colonne de jointure s'il n'en existe pas déjà une. Si tel est le cas, cette annotation peut être utilisée pour nommer la colonne de jointure.


MappedBy

Le paramètre MappedBy sert à indiquer à JPA: Ne créez PAS une autre table de jointure, car la relation est déjà mappée par l'entité opposée . de cette relation.



N'oubliez pas: MappedBy est une propriété des annotations de relation dont le but est de générer un mécanisme permettant de mettre en relation deux entités qui, par défaut, créent une table de jointure. MappedBy interrompt ce processus dans un sens.

L'entité n'utilisant pas MappedBy est dite propriétaire de la relation car les mécanismes de la correspondance sont dictés dans sa classe par l'utilisation d'une des trois annotations de mappage avec le champ de clé étrangère. Cela spécifie non seulement la nature du mappage, mais indique également la création d'une table de jointure. En outre, l'option de suppression de la table de jointure existe également en appliquant l'annotation @JoinColumn sur la clé étrangère, qui la conserve à l'intérieur de la table de l'entité propriétaire.

En résumé: @JoinColumn crée une nouvelle colonne de jointure ou renomme une colonne existante; tandis que le paramètre MappedBy travaille en collaboration avec les annotations de relation de l'autre classe (enfant) afin de créer un mappage via une table de jointure ou en créant une colonne de clé étrangère dans la table associée de l'entité propriétaire.

Pour illustrer le fonctionnement de MapppedBy, considérons le code ci-dessous. Si le paramètre MappedBy devait être supprimé, Hibernate créerait en réalité DEUX tables de jointure! Pourquoi? Parce qu'il existe une symétrie dans les relations plusieurs à plusieurs et qu'Hibernate n'a aucune raison de choisir une direction plutôt qu'une autre.

Nous utilisons donc MappedBy pour dire à Hibernate, nous avons choisi l'autre entité pour dicter le mappage de la relation entre les deux entités.

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    private List<Drivers> drivers;
}

L'ajout de @JoinColumn (name = "driverID") à la classe de propriétaire (voir ci-dessous) empêchera la création d'une table de jointure et créera une colonne de clé étrangère driverID dans la table Cars pour construire un mappage:

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name = "driverID")
    private List<Drivers> drivers;
}
11
IqbalHamid

JPA est une API en couches, les différents niveaux ont leurs propres annotations. Le niveau le plus élevé est le niveau (1) d'entité qui décrit les classes persistantes. Le niveau (2) de la base de données relationnelle suppose que les entités sont mappées sur une base de données relationnelle et (3) le modèle Java.

Annotations de niveau 1: @Entity, @Id, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany. Vous pouvez introduire la persistance dans votre application en utilisant uniquement ces annotations de haut niveau. Mais vous devez ensuite créer votre base de données en fonction des hypothèses retenues par JPA. Ces annotations spécifient le modèle d'entité/relation.

Annotations de niveau 2: @Table, @Column, @JoinColumn, ... Influencez le mappage d'entités/propriétés sur les tables/colonnes de la base de données relationnelle si vous n'êtes pas satisfait des valeurs par défaut de JPA ou si vous devez mapper sur une base de données existante. Ces annotations peuvent être considérées comme des annotations d'implémentation. Elles spécifient comment le mappage doit être effectué.

À mon avis, il est préférable de coller autant que possible aux annotations de niveau supérieur, puis d'introduire les annotations de niveau inférieur en fonction des besoins.

Pour répondre aux questions: le @ OneToMany/mappedBy est le plus agréable car il utilise uniquement les annotations du domaine de l’entité. Le @ oneToMany/@ JoinColumn convient également, mais il utilise une annotation d'implémentation lorsque cela n'est pas strictement nécessaire.

0
Bruno Ranschaert