Quelqu'un peut-il expliquer mappedBy dans JPA et Hibernate?

175

Je suis nouveau dans l'hibernation et j'ai besoin d'utiliser des relations un-à-plusieurs et plusieurs-à-un. C'est une relation bidirectionnelle dans mes objets, de sorte que je puisse traverser dans les deux sens. mappedByest la manière recommandée de s'y prendre, cependant, je ne pouvais pas la comprendre. Quelqu'un peut-il expliquer:

  • quelle est la manière recommandée de l'utiliser?
  • quel but résout-il?

Pour mon exemple, voici mes cours avec des annotations:

  • Airline Possède de nombreux AirlineFlights
  • Beaucoup AirlineFlights appartiennent à ONE Airline

Compagnie aérienne :

@Entity 
@Table(name="Airline")
public class Airline {
    private Integer idAirline;
    private String name;

    private String code;

    private String aliasName;
    private Set<AirlineFlight> airlineFlights = new HashSet<AirlineFlight>(0);

    public Airline(){}

    public Airline(String name, String code, String aliasName, Set<AirlineFlight> flights) {
        setName(name);
        setCode(code);
        setAliasName(aliasName);
        setAirlineFlights(flights);
    }

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="IDAIRLINE", nullable=false)
    public Integer getIdAirline() {
        return idAirline;
    }

    private void setIdAirline(Integer idAirline) {
        this.idAirline = idAirline;
    }

    @Column(name="NAME", nullable=false)
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = DAOUtil.convertToDBString(name);
    }

    @Column(name="CODE", nullable=false, length=3)
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = DAOUtil.convertToDBString(code);
    }

    @Column(name="ALIAS", nullable=true)
    public String getAliasName() {
        return aliasName;
    }
    public void setAliasName(String aliasName) {
        if(aliasName != null)
            this.aliasName = DAOUtil.convertToDBString(aliasName);
    }

    @OneToMany(fetch=FetchType.LAZY, cascade = {CascadeType.ALL})
    @JoinColumn(name="IDAIRLINE")
    public Set<AirlineFlight> getAirlineFlights() {
        return airlineFlights;
    }

    public void setAirlineFlights(Set<AirlineFlight> flights) {
        this.airlineFlights = flights;
    }
}

Vols aériens:

@Entity
@Table(name="AirlineFlight")
public class AirlineFlight {
    private Integer idAirlineFlight;
    private Airline airline;
    private String flightNumber;

    public AirlineFlight(){}

    public AirlineFlight(Airline airline, String flightNumber) {
        setAirline(airline);
        setFlightNumber(flightNumber);
    }

    @Id
    @GeneratedValue(generator="identity")
    @GenericGenerator(name="identity", strategy="identity")
    @Column(name="IDAIRLINEFLIGHT", nullable=false)
    public Integer getIdAirlineFlight() {
        return idAirlineFlight;
    }
    private void setIdAirlineFlight(Integer idAirlineFlight) {
        this.idAirlineFlight = idAirlineFlight;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="IDAIRLINE", nullable=false)
    public Airline getAirline() {
        return airline;
    }
    public void setAirline(Airline airline) {
        this.airline = airline;
    }

    @Column(name="FLIGHTNUMBER", nullable=false)
    public String getFlightNumber() {
        return flightNumber;
    }
    public void setFlightNumber(String flightNumber) {
        this.flightNumber = DAOUtil.convertToDBString(flightNumber);
    }
}

ÉDITER:

Schéma de la base de données:

AirlineFlights a l'idAirline comme ForeignKey et Airline n'a pas d'idAirlineFlights. Cela fait, AirlineFlights en tant que propriétaire / entité d'identification?

Théoriquement, j'aimerais que la compagnie aérienne soit propriétaire de la compagnie aérienneFlights.

entrez la description de l'image ici

brainydexter
la source

Réponses:

151

En spécifiant @JoinColumnsur les deux modèles, vous n'avez pas de relation bidirectionnelle. Vous avez deux relations à sens unique, et une cartographie très déroutante de celle-ci. Vous dites aux deux modèles qu'ils «possèdent» la colonne IDAIRLINE. Un seul d'entre eux devrait vraiment le faire! La chose `` normale '' est de supprimer @JoinColumncomplètement le hors du @OneToManycôté et d'ajouter à la place mappedBy au @OneToMany.

@OneToMany(cascade = CascadeType.ALL, mappedBy="airline")
public Set<AirlineFlight> getAirlineFlights() {
    return airlineFlights;
}

Cela indique à Hibernate: "Allez chercher la propriété du bean nommée« compagnie aérienne »sur la chose dont j'ai une collection pour trouver la configuration."

Affe
la source
2
Je suis un peu confus par votre description à la fin de mappedBy. La manière dont les choses sont organisées dans db a-t-elle une importance? @DB: AirlineFlights a idAirline comme clé étrangère. Les compagnies aériennes ont juste idAirline comme clé primaire et ne conservent aucune information sur AirlineFlights @ DB.
brainydexter
10
Oui, c'est important. Le nom dans mappedBy indique à Hibernate où trouver la configuration de JoinColumn. (Sur la méthode getAirline () de AirlineFlight.) La façon dont vous l'avez mappé en mettant JoinColumn sur la compagnie aérienne, vous dites à Airline qu'il est responsable de maintenir les valeurs dans l'autre tableau. Il est possible de dire à une entité qu'elle "possède" une colonne dans une table différente et est responsable de sa mise à jour. Ce n'est pas quelque chose que vous voulez généralement faire et peut causer des problèmes avec l'ordre d'exécution des instructions SQL.
Affe le
S'il vous plaît voir la modification. Au niveau de la base de données, la table AirFlight possède idAirline en tant que colonne de clé étrangère. Par conséquent, JoinColumn doit être placé sur la classe / table de la compagnie aérienneFlight dans le ManytoOne correspondant, car il possède cette colonne?
brainydexter
Oui, je recommanderais de le faire de cette façon. C'est l'option la moins compliquée et vous ne semblez pas avoir besoin d'autre chose.
Affe
"Enlevez le @JoinColumn du côté @OneToMany entièrement" vous voulez dire du @ManyToOnecôté, non?
nbro
284

MappedBy signale l'hibernation que la clé de la relation se trouve de l'autre côté.

Cela signifie que même si vous liez 2 tables ensemble, seule 1 de ces tables a une contrainte de clé étrangère sur l'autre. MappedBy vous permet de créer un lien depuis la table ne contenant pas la contrainte vers l'autre table.

Kurt Du Bois
la source
3
pouvez-vous clarifier un peu plus?
Alexander Suraphel
1
@Kurt Du Bois, pourquoi utiliseriez-vous mappedByplutôt que de définir un bidirectionnel (avec des contraintes Foreign_key de chaque côté)?
Kevin Meredith
6
Parce que parfois, cela n'a pas de sens de mettre une clé des deux côtés. Disons par exemple que vous avez une entreprise et un portable. Un portable n'appartiendra qu'à une seule entreprise, mais une entreprise aura plusieurs portables.
Kurt Du Bois
Désolé au responsable de l'édition d'avoir annulé ma réponse, mais il n'y avait en fait aucune valeur ajoutée dans votre modification. La dernière phrase n'avait même pas de sens.
Kurt Du Bois
@KurtDuBois donc mappé par viens seulement en image comment créer votre base de données, c'est-à-dire que vous utilisez mappedby ou non hibernate côté java se comporte de la même manière.
22

mappedbyparle de lui-même, il dit à hibernate de ne pas mapper ce champ. il est déjà mappé par ce champ [name = "field"].
le champ est dans l'autre entité (name of the variable in the class not the table in the database).

Si vous ne le faites pas, hibernate mappera ces deux relations car ce n'est pas la même relation

nous devons donc dire à hibernate de faire le mappage d'un seul côté et de coordonner entre eux.

Charif DZ
la source
est mappedBy est facultatif? Parce que sans utiliser mappedBy, j'obtiens le même résultat, c'est-à-dire un mappage d'objets bidirectionnel
Freelancer
vous ne pouvez pas utiliser on2many et many2one sans utiliser mappedBy dans l'un des côtés même chose pour beaucoup2many vous devez utiliser mappedBy d'un côté
Charif DZ
Merci d'avoir indiqué ce que signifie la valeur d'attribut, c'est-à-dire le nom du champ dans l'autre table.
Gab 是 好人
2
Peut-être que l'hibernation ne parle pas toujours d'elle-même, mais quand elle le fait, au moins elle utilise la ponctuation
Amalgovinus
1
Pour moi, cela ne parle pas de lui-même; à l'inverse, c'est très déroutant. Regardez simplement le nombre de questions concernant ce qui est réellement mappedByet inversedBy. D' autres ORM utilisent beaucoup plus intelligents belongsToMany, les hasManyattributs.
Jan Bodnar le
12

mappedby = "objet d'entité de même classe créé dans une autre classe"

Remarque: -Mappé par ne peut être utilisé que dans une classe car une table doit contenir une contrainte de clé étrangère. si mappé par peut être appliqué des deux côtés, il supprime la clé étrangère des deux tables et sans clé étrangère, il n'y a pas de relation entre deux tables.

Remarque: - il peut être utilisé pour les annotations suivantes: - 1. @ OneTone 2. @ OneToMany 3. @ ManyToMany

Remarque --- Il ne peut pas être utilisé pour l'annotation suivante: - 1. @ ManyToOne

Dans un à un: - Exécutez de n'importe quel côté du mappage mais exécutez d'un seul côté. Il supprimera la colonne supplémentaire de contrainte de clé étrangère sur la table sur laquelle elle est appliquée.

Pour par exemple. Si nous appliquons mappé par dans la classe Employee sur l'objet Employee, la clé étrangère de la table Employee sera supprimée.

ks gujjar
la source
12

Relation de table vs relation d'entité

Dans un système de base de données relationnelle, une one-to-manyrelation de table se présente comme suit:

Relation de table <code> un-à-plusieurs </code>

Notez que la relation est basée sur la colonne Clé étrangère (par exemple, post_id) dans la table enfant.

Il existe donc une seule source de vérité lorsqu'il s'agit de gérer une one-to-manyrelation de table.

Maintenant, si vous prenez une relation d'entité bidirectionnelle qui correspond à la one-to-manyrelation de table que nous avons vue précédemment:

Association d'entités bidirectionnelles <code> One-To-Many </code>

Si vous regardez le diagramme ci-dessus, vous pouvez voir qu'il existe deux façons de gérer cette relation.

Dans l' Postentité, vous avez la commentscollection:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

Et, dans le PostComment, l' postassociation est mappée comme suit:

@ManyToOne(
    fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;

Étant donné qu'il existe deux façons de représenter la colonne de clé étrangère, vous devez définir quelle est la source de vérité lorsqu'il s'agit de traduire le changement d'état d'association en modification de la valeur de la colonne de clé étrangère équivalente.

Mappé par

L' mappedByattribut indique que le @ManyToOnecôté est en charge de la gestion de la colonne de clé étrangère, et la collection est utilisée uniquement pour récupérer les entités enfants et pour mettre en cascade les changements d'état de l'entité parente aux enfants (par exemple, la suppression du parent devrait également supprimer les entités enfants).

Synchronisez les deux côtés d'une association bidirectionnelle

Désormais, même si vous avez défini l' mappedByattribut et que l' @ManyToOneassociation côté enfant gère la colonne de clé étrangère, vous devez toujours synchroniser les deux côtés de l'association bidirectionnelle.

La meilleure façon de le faire est d'ajouter ces deux méthodes utilitaires:

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

Les méthodes addCommentet removeCommentgarantissent que les deux côtés sont synchronisés. Donc, si nous ajoutons une entité enfant, l'entité enfant doit pointer vers le parent et l'entité parente doit avoir l'enfant contenu dans la collection enfant.

Pour plus d'informations sur la meilleure façon de synchroniser tous les types d'association d'entités bidirectionnelles, consultez cet article .

Vlad Mihalcea
la source
0

Vous avez commencé avec le mappage ManyToOne, puis vous avez également mis le mappage OneToMany pour la manière BiDirectionnelle. Ensuite, du côté OneToMany (généralement votre table / classe parent), vous devez mentionner "mappedBy" (le mappage est effectué par et dans la table / classe enfant), donc hibernate ne créera pas de table de mappage EXTRA dans DB (comme TableName = parent_child).

Akshay N. Shelke
la source