Quel est le «côté propriétaire» d'un mappage ORM?

129

Que signifie exactement le côté propriétaire ? Qu'est-ce qu'une explication avec quelques exemples de cartographie ( un à plusieurs, un à un, plusieurs à un )?

Le texte suivant est un extrait de la description de @OneToOne dans la documentation Java EE 6. Vous pouvez voir le côté propriétaire du concept .

Définit une association à valeur unique avec une autre entité qui a une multiplicité un-à-un. Il n'est normalement pas nécessaire de spécifier explicitement l'entité cible associée car elle peut généralement être déduite du type de l'objet référencé. Si la relation est bidirectionnelle, le côté non propriétaire doit utiliser l'élément mappedBy de l'annotation OneToOne pour spécifier le champ de relation ou la propriété du côté propriétaire.

Juste un apprenant
la source
5
J'étais perdu jusqu'à ce que je lis ceci: javacodegeeks.com/2013/04/…
darga33
2
La table DB avec la colonne de clé étrangère est traitée comme propriétaire. Ainsi, l'entité commerciale représentant cette table DB est le propriétaire (côté propriétaire) de cette relation. Pas nécessairement, mais la plupart des cas Owning-side aura l'annotation @JoinColumn.
Diablo

Réponses:

202

Pourquoi la notion de côté propriétaire est-elle nécessaire:

L'idée d'un côté propriétaire d'une relation bidirectionnelle vient du fait que dans les bases de données relationnelles, il n'y a pas de relations bidirectionnelles comme dans le cas des objets. Dans les bases de données, nous n'avons que des relations unidirectionnelles - des clés étrangères.

Quelle est la raison du nom «côté propriétaire»?

Le côté propriétaire de la relation suivie par Hibernate est le côté de la relation qui possède la clé étrangère dans la base de données.

Quel est le problème que résout la notion de côté propriétaire?

Prenons un exemple de deux entités mappées sans déclarer de côté propriétaire:

@Entity
@Table(name="PERSONS")
public class Person {
    @OneToMany
    private List<IdDocument>  idDocuments;
}

@Entity
@Table(name="ID_DOCUMENTS")
public class IdDocument {
    @ManyToOne
    private Person person;
}

D'un point de vue OO, ce mapping définit non pas une relation bidirectionnelle, mais deux relations unidirectionnelles distinctes.

Le mappage créerait non seulement des tables PERSONSet ID_DOCUMENTS, mais créerait également une troisième table d'association PERSONS_ID_DOCUMENTS:

CREATE TABLE PERSONS_ID_DOCUMENTS
(
  persons_id bigint NOT NULL,
  id_documents_id bigint NOT NULL,
  CONSTRAINT fk_persons FOREIGN KEY (persons_id) REFERENCES persons (id),
  CONSTRAINT fk_docs FOREIGN KEY (id_documents_id) REFERENCES id_documents (id),
  CONSTRAINT pk UNIQUE (id_documents_id)
)

Notez la clé primaire pksur ID_DOCUMENTSseulement. Dans ce cas, Hibernate suit les deux côtés de la relation indépendamment: Si vous ajoutez un document à la relation Person.idDocuments, il insère un enregistrement dans la table d'association PERSON_ID_DOCUMENTS.

Par contre, si nous appelons idDocument.setPerson(person), nous changeons la clé étrangère person_id sur la table ID_DOCUMENTS. Hibernate crée deux relations unidirectionnelles (clé étrangère) sur la base de données, pour implémenter une relation d'objet bidirectionnelle.

Comment la notion de côté propriétaire résout le problème:

Plusieurs fois ce que nous voulons est seulement une clé étrangère sur la table ID_DOCUMENTSvers PERSONSet la table d'association supplémentaire.

Pour résoudre ce problème, nous devons configurer Hibernate pour arrêter le suivi des modifications sur la relation Person.idDocuments. Hibernate ne devrait suivre que l' autre côté de la relation IdDocument.person, et pour ce faire, nous ajoutons mappedBy :

@OneToMany(mappedBy="person")
private List<IdDocument>  idDocuments;

Qu'est-ce que cela signifie mappedBy?

Cela signifie quelque chose comme: "les modifications de ce côté de la relation sont déjà mappées par l'autre côté de la relation IdDocument.person, donc pas besoin de la suivre ici séparément dans une table supplémentaire."

Y a-t-il des GOTCHA, des conséquences?

En utilisant mappedBy , si nous appelons seulement person.getDocuments().add(document), la clé étrangère dans ID_DOCUMENTSne sera PAS liée au nouveau document, car ce n'est pas le côté propriétaire / suivi de la relation!

Pour lier le document à la nouvelle personne, vous devez appeler explicitement document.setPerson(person), car c'est le côté propriétaire de la relation.

Lors de l'utilisation de mappedBy , il est de la responsabilité du développeur de savoir quel est le côté propriétaire et de mettre à jour le bon côté de la relation afin de déclencher la persistance de la nouvelle relation dans la base de données.

Université angulaire
la source
17
La meilleure réponse que je trouve qui explique la doctrine «mappedBy» + «inversedBy».
Kurt Zhong
1
Merci d'avoir spécifié les mappages et la raison du concept.
Mohnish
1
Je ne sais pas si les choses ont changé, mais sur Hibernate 5.0.9.Final si j'appelle seulement person.getDocuments().add(document), hibernate met à jour la clé étrangère dans ID_DOCUMENTS.
K.Nicholas
1
@Karl Nicholas, salut, d'accord avec vous, nous avons un attribut de cascade sur l' @OneToManyannotation qui peut être défini sur PERSIST. Dans ce cas, la mise en veille prolongée sauvegardera toutes les entités liées dans la base de données. Quelqu'un peut-il clarifier ceci - pourquoi l'auteur dit que hibernate ne suivra pas les changements du côté non propriétaire - mais qu'en fait hibernated effectue le suivi?
Oleksandr Papchenko
3
cascade indique au fournisseur de sauvegarder les entités enfants même si l'entité parente ne les possède pas, afin de modifier efficacement la règle. Si vous aviez (ou avez) mappedBy = child.field et que vous n'aviez PAS de cascade, les enfants de la liste ne seraient pas enregistrés. De plus, si vous n'avez pas mappedBy AND n'a pas de cascade, le parent est propriétaire de la relation et si vous mettez de NOUVEAUX enfants dans la liste, puis enregistrez le parent, il lancera une exception car les nouveaux ID enfants ne sont pas disponibles pour être enregistré dans la table de jointure. J'espère que cela clarifie les choses ... :)
K.Nicholas
142

Vous pouvez imaginer que le côté propriétaire est l'entité qui a la référence à l'autre. Dans votre extrait, vous avez une relation individuelle. Puisqu'il s'agit d'une relation symétrique , vous finirez par avoir que si l'objet A est en relation avec l'objet B, alors l'inverse est également vrai.

Cela signifie que sauvegarder dans l'objet A une référence à l'objet B et sauvegarder dans l'objet B une référence à l'objet A sera redondant: c'est pourquoi vous choisissez quel objet "possède" l'autre ayant la référence à lui.

Lorsque vous avez une relation un-à-plusieurs, les objets liés à la partie "plusieurs" seront du côté propriétaire, sinon vous auriez à stocker de nombreuses références d'un seul objet à une multitude. Pour éviter cela, chaque objet de la deuxième classe aura un pointeur vers celui auquel il se réfère (ils sont donc du côté propriétaire).

Pour une relation plusieurs à plusieurs, puisque vous aurez besoin d'une table de mappage séparée de toute façon, il n'y aura pas de côté propriétaire.

En conclusion, le côté propriétaire est l'entité qui a la référence à l'autre.

Jack
la source
6
Merci pour la clarification.
Juste un apprenant
2
cela pourrait aider de voir ci-dessous ma réponse pour les raisons des noms `` mappedBy '' et `` propriétaire '', que se passe-t-il si nous ne définissons pas un côté propriétaire, GOTCHAs, espérons que cela aide
Angular University
5
Eh bien, la réponse est généralement correcte, je suppose. Mais pour Hibernate au moins, même les relations plusieurs-à-plusieurs ont un côté propriétaire. Cela a des implications pour le comportement de mise à jour par exemple. Regardez de près la section 4 («Update Hibernate Model Class») de ce tutoriel: viralpatel.net/blogs/…
Pfiver
11
Cette réponse déroute plus qu'elle n'aide. À quoi cela sert-il de dire que "vous pouvez imaginer que le côté propriétaire est l'entité qui a la référence à l'autre" alors que dans des relations bidirectionnelles, les deux objets Entity auront une référence l'un à l'autre? De plus, "Pour une relation plusieurs-à-plusieurs, puisque vous aurez besoin d'une table de mappage séparée de toute façon, il n'y aura pas de côté propriétaire" est tout simplement incorrect: les @ManyToManyrelations ont aussi des côtés propriétaires. De même, les @OneToManyrelations peuvent utiliser des tables de jointure et vous devez toujours spécifier un côté propriétaire.
DavidS
5
En gros, c'est une réponse mignonne et agréable qui a des votes positifs car elle est plus facile à comprendre que la vérité.
DavidS