Différence entre un-à-plusieurs, plusieurs-à-un et plusieurs-à-plusieurs?

145

Ok, donc c'est probablement une question triviale mais j'ai du mal à visualiser et à comprendre les différences et quand les utiliser. Je ne sais pas non plus comment des concepts tels que les mappages unidirectionnels et bidirectionnels affectent les relations un-à-plusieurs / plusieurs-à-plusieurs. J'utilise Hibernate en ce moment, donc toute explication liée à l'ORM sera utile.

À titre d'exemple, disons que j'ai la configuration suivante:

public class Person{
    private Long personId;
    private Set<Skill> skills;
    //Getters and setters
}

public class Skill{
    private Long skillId;
    private String skillName;
    //Getters and setters
}

Alors, dans ce cas, quel type de cartographie aurais-je? Les réponses à cet exemple spécifique sont certainement appréciées, mais j'aimerais aussi vraiment avoir un aperçu du moment où utiliser un à plusieurs et plusieurs à plusieurs et quand utiliser une table de jointure par rapport à une colonne de jointure et unidirectionnel par rapport à bidirectionnel.

Ian Dallas
la source
11
On dirait que tout le monde ne répond qu'à un à plusieurs par rapport à plusieurs à plusieurs. Regardez ma réponse pour un-à-plusieurs vs plusieurs-à-un.
Alexander Suraphel

Réponses:

165

Un-à-plusieurs : une personne a plusieurs compétences, une compétence n'est pas réutilisée entre les personnes

  • Unidirectionnel : une personne peut référencer directement des compétences via son ensemble
  • Bidirectionnel : chaque compétence "enfant" a un seul pointeur vers la personne (qui n'est pas indiqué dans votre code)

Plusieurs à plusieurs : une personne a plusieurs compétences, une compétence est réutilisée entre les personnes

  • Unidirectionnel : une personne peut référencer directement des compétences via son ensemble
  • Bidirectionnel : une compétence a un ensemble de personnes qui s'y rapportent.

Dans une relation un-à-plusieurs, un objet est le "parent" et un autre est "l'enfant". Le parent contrôle l'existence de l'enfant. Dans un Many-To-Many, l'existence de l'un ou l'autre type dépend de quelque chose en dehors des deux (dans le contexte d'application plus large).

Votre sujet (domaine) doit dicter si la relation est un-à-plusieurs ou plusieurs-à-plusieurs - cependant, je trouve que rendre la relation unidirectionnelle ou bidirectionnelle est une décision d'ingénierie qui échange la mémoire, le traitement, les performances , etc.

Ce qui peut prêter à confusion, c'est qu'une relation bidirectionnelle plusieurs-à-plusieurs n'a pas besoin d'être symétrique! Autrement dit, un groupe de personnes pourrait désigner une compétence, mais la compétence n'a pas besoin de se rapporter uniquement à ces personnes. C'est généralement le cas, mais une telle symétrie n'est pas une exigence. Prenez l'amour, par exemple - il est bidirectionnel ("I-Love", "Loves-Me"), mais souvent asymétrique ("Je l'aime, mais elle ne m'aime pas")!

Tous ces éléments sont bien pris en charge par Hibernate et JPA. Rappelez-vous simplement qu'Hibernate ou tout autre ORM ne se soucie pas du maintien de la symétrie lors de la gestion des relations plusieurs-à-plusieurs bidirectionnelles ... tout dépend de l'application.

HDave
la source
Pour clarifier, toute relation peut être uni- ou bidirectionnelle dans votre BL ou dans votre mappage O / R (indépendamment les uns des autres, même!).
jyoungdev
4
L'exemple "LOVE" vient de le clarifier. ManyToMany est mon type de cartographie.
Abdullah Khan
1
Super. Cela l'explique si bien (et dans le contexte de l'exemple d'OP)
Anupam
1
Ne répond pas correctement à la question. vous manquez le plusieurs à une partie. même si un à plusieurs et plusieurs à un est une question de perception, cette réponse ne le mentionne pas.
Manzur Alahi le
249

On dirait que tout le monde répond One-to-manyvs Many-to-many.:

La différence entre One-to-many, Many-to-oneet Many-to-Manyest:

One-to-manyvs Many-to-oneest une question de perspective . Unidirectionalvs Bidirectionaln'affectera pas le mappage mais fera la différence sur la façon dont vous pouvez accéder à vos données.

  • Dans Many-to-onele manycôté gardera la référence du onecôté. Un bon exemple est «Un État a des villes». Dans ce cas, Statec'est le côté unique et Cityle côté multiple. Il y aura une colonne state_iddans le tableau cities.

En unidirectionnel , la Personclasse aura List<Skill> skillsmais Skilln'aura pas Person person. En bidirectionnel , les deux propriétés sont ajoutées et cela vous permet d'accéder à Personune compétence donnée (ie skill.person).

  • Dans One-to-Manyun côté sera notre point de référence. Par exemple, "Un utilisateur a une adresse". Dans ce cas, nous pourrions avoir trois colonnes address_1_id, address_2_idet / address_3_idou une table de recherche avec une contrainte unique sur user_idet address_id.

En unidirectionnel , un Useraura Address address. Bidirectionnel aura un supplément List<User> usersdans la Addressclasse.

  • Dans les Many-to-Manymembres de chaque parti peuvent tenir référence à un nombre arbitraire de membres de l'autre parti. Pour ce faire, une table de consultation est utilisée. La relation entre les médecins et les patients en est un exemple. Un médecin peut avoir de nombreux patients et vice versa.
Alexandre Suraphel
la source
27
cela devrait être la réponse acceptée, la plupart des autres réponses manquent la question.
arg20
Le premier exemple est incorrect. Si vous avez A Person et Person a @OneToMany with Skills, cette table preson_skills aurait une contrainte unique sur skill_id. Ainsi, une compétence serait mappée à une seule personne. Et vous ne pouvez pas extraire s.persons, car il n'y a ques.person
Иван Николайчук
En fait, la One-to-manyrelation que vous décrivez est une Many-to-manyrelation car elle personfait référence à beaucoup skillsmaisskill ne garde pas la référence à une personne en particulier et beaucoup personspeuvent avoir une référence identique skill. Et votre Many-to-onerelation est en fait One-to-manyparce que chaque compétence ne fait référence qu'à une seule personcar un enfant n'a qu'une seule mère.
mixel
@mixel votre commentaire a fini par me faire réécrire la plupart de ma réponse. Veuillez le vérifier à nouveau! Merci
Alexander Suraphel
Vous faites la distinction entre un-à-plusieurs et plusieurs-à-un par la signification du côté "Un". Dans les deux exemples, «l'utilisateur» est l'entité la plus significative. Donc, quand il s'agit du "Un" (utilisateurs et compétences, skillsa person_id), alors vous l'appelez un-à-plusieurs, mais quand c'est du côté "Plusieurs" (utilisateurs et adresses, usersa address_id), vous l'appelez plusieurs-à-un. Mais structurellement, les deux cas sont identiques et appelés un-à-plusieurs.
mixel
38

1) Les cercles sont Entités / POJO / Haricots

2) deg est une abréviation de degré comme dans les graphiques (nombre d'arêtes)

PK = clé primaire, FK = clé étrangère

Notez la contradiction entre le diplôme et le nom du côté. Beaucoup correspond à degré = 1 tandis que Un correspond à degré> 1.

Illustration de un-à-plusieurs plusieurs-à-un

jhegedus
la source
1
J'adore la façon dont il lie le graphique d'objets aux tableaux dans les deux sens.
Dmitry Minkovsky
3
Regardez les nerds, voici à quoi ressemble l' ÉCRITURE DU PROGRAMMEUR : D
Mehraj Malik
Je vois ce que vous avez fait ici.
Nick Gallimore
8

Jetez un œil à cet article: Mappage des relations d'objets

Il existe deux catégories de relations d'objet dont vous devez vous préoccuper lors du mappage. La première catégorie est basée sur la multiplicité et comprend trois types:

*One-to-one relationships.  This is a relationship where the maximums of each of its multiplicities is one, an example of which is holds relationship between Employee and Position in Figure 11.  An employee holds one and only one position and a position may be held by one employee (some positions go unfilled).
*One-to-many relationships. Also known as a many-to-one relationship, this occurs when the maximum of one multiplicity is one and the other is greater than one.  An example is the works in relationship between Employee and Division.  An employee works in one division and any given division has one or more employees working in it.
*Many-to-many relationships. This is a relationship where the maximum of both multiplicities is greater than one, an example of which is the assigned relationship between Employee and Task.  An employee is assigned one or more tasks and each task is assigned to zero or more employees. 

La deuxième catégorie est basée sur la directionnalité et contient deux types, les relations unidirectionnelles et les relations bidirectionnelles.

*Uni-directional relationships.  A uni-directional relationship when an object knows about the object(s) it is related to but the other object(s) do not know of the original object.  An example of which is the holds relationship between Employee and Position in Figure 11, indicated by the line with an open arrowhead on it.  Employee objects know about the position that they hold, but Position objects do not know which employee holds it (there was no requirement to do so).  As you will soon see, uni-directional relationships are easier to implement than bi-directional relationships.
*Bi-directional relationships.  A bi-directional relationship exists when the objects on both end of the relationship know of each other, an example of which is the works in relationship between Employee and Division.  Employee objects know what division they work in and Division objects know what employees work in them. 
Alejandrobog
la source
2
this occurs when the maximum of one multiplicity is one and the other is greater than onelolwut?
serg
3

C'est une question très courante, donc cette réponse est basée sur cet article que j'ai écrit sur mon blog.

Un à plusieurs

La relation de table un à plusieurs se présente comme suit:

Un à plusieurs

Dans un système de base de données relationnelle, une relation de table un-à-plusieurs lie deux tables sur la base d'un Foreign Key colonne de l'enfant qui fait référence à la ligne Primary Keyde la table parent.

Dans le diagramme de tableau ci-dessus, la post_idcolonne du post_commenttableau a une Foreign Keyrelation avec la colonne d' postID de table Primary Key:

ALTER TABLE
    post_comment
ADD CONSTRAINT
    fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post

Annotation @ManyToOne

La meilleure façon de mapper la relation de table un à plusieurs consiste à utiliser @ManyToOne annotation.

Dans notre cas, l'entité enfant PostCommentmappe la post_idcolonne de clé étrangère à l'aide de l' @ManyToOneannotation:

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

}

Utilisation du JPA @OneToMany annotation

Ce @OneToManyn'est pas parce que vous avez la possibilité d'utiliser l' annotation que cela doit être l'option par défaut pour chaque un-à-plusieurs relation de base données . Le problème avec les collections est que nous ne pouvons les utiliser que lorsque le nombre d'enregistrements enfants est plutôt limité.

La meilleure façon de mapper une @OneToManyassociation est de s'appuyer sur le @ManyToOnecôté pour propager tous les changements d'état d'entité:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

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

    //Constructors, getters and setters removed for brevity

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

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

L'entité parente,, Postprésente deux méthodes utilitaires (par exemple addCommentet removeComment) qui sont utilisées pour synchroniser les deux côtés de l'association bidirectionnelle. Vous devez toujours fournir ces méthodes chaque fois que vous travaillez avec une association bidirectionnelle car, sinon, vous risquez des problèmes de propagation d'état très subtils .

L' @OneToManyassociation unidirectionnelle est à éviter car elle est moins efficace que l'utilisation @ManyToOneou l' @OneToManyassociation bidirectionnelle .

Pour plus de détails sur la meilleure façon de cartographier la @OneToManyrelation avec JPA et Hibernate, consultez cet article .

Un par un

La relation de table un-à-un se présente comme suit:

Un par un

Dans un système de base de données relationnelle, une relation de table un-à-un lie deux tables en fonction d'une Primary Keycolonne de l'enfant qui fait également Foreign Keyréférence à la ligne Primary Keyde la table parent.

Par conséquent, nous pouvons dire que la table enfant partage le Primary Keyavec la table parent.

Dans le diagramme ci-dessus, la idcolonne du post_detailstableau a également une Foreign Keyrelation avec la colonne du posttableau id Primary Key:

ALTER TABLE
    post_details
ADD CONSTRAINT
    fk_post_details_id
FOREIGN KEY (id) REFERENCES post

Utilisation de JPA @OneToOneavec des @MapsIdannotations

La meilleure façon de cartographier une @OneToOnerelation est d'utiliser @MapsId. De cette façon, vous n'avez même pas besoin d'une association bidirectionnelle puisque vous pouvez toujours récupérer l' PostDetailsentité en utilisant l' Postidentifiant d'entité.

Le mappage ressemble à ceci:

[code language = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") classe publique PostDetails {

@Id
private Long id;

@Column(name = "created_on")
private Date createdOn;

@Column(name = "created_by")
private String createdBy;

@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id")
private Post post;

public PostDetails() {}

public PostDetails(String createdBy) {
    createdOn = new Date();
    this.createdBy = createdBy;
}

//Getters and setters omitted for brevity

} [/ code]

De cette façon, la idpropriété sert à la fois de clé primaire et de clé étrangère. Vous remarquerez que la @Idcolonne n'utilise plus d' @GeneratedValueannotation puisque l'identifiant est rempli avec l'identifiant de l' postassociation.

Pour plus de détails sur la meilleure façon de cartographier la @OneToOnerelation avec JPA et Hibernate, consultez cet article .

Plusieurs à plusieurs

La relation de table plusieurs à plusieurs se présente comme suit:

Plusieurs à plusieurs

Dans un système de base de données relationnelle, une relation de table plusieurs à plusieurs lie deux tables parentes via une table enfant qui contient deux Foreign Keycolonnes référençant les Primary Keycolonnes des deux tables parentes.

Dans le diagramme de tableau ci-dessus, la post_idcolonne du post_tagtableau a également une Foreign Keyrelation avec la colonne d' postid de table Primary Key:

ALTER TABLE
    post_tag
ADD CONSTRAINT
    fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post

Et, la tag_idcolonne de la post_tagtable a une Foreign Keyrelation avec la colonne d' tagID de table Primary Key:

ALTER TABLE
    post_tag
ADD CONSTRAINT
    fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag

Utilisation du @ManyToManymappage JPA

Voici comment mapper la many-to-manyrelation de table avec JPA et Hibernate:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @ManyToMany(cascade = { 
        CascadeType.PERSIST, 
        CascadeType.MERGE
    })
    @JoinTable(name = "post_tag",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private Set<Tag> tags = new HashSet<>();

    //Getters and setters ommitted for brevity

    public void addTag(Tag tag) {
        tags.add(tag);
        tag.getPosts().add(this);
    }

    public void removeTag(Tag tag) {
        tags.remove(tag);
        tag.getPosts().remove(this);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Post)) return false;
        return id != null && id.equals(((Post) o).getId());
    }

    @Override
    public int hashCode() {
        return 31;
    }
}

@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String name;

    @ManyToMany(mappedBy = "tags")
    private Set<Post> posts = new HashSet<>();

    //Getters and setters ommitted for brevity

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Tag tag = (Tag) o;
        return Objects.equals(name, tag.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}
  1. L' tagsassociation dans l' Postentité définit uniquement les types PERSISTet MERGEcascade. Comme expliqué dans cet article , la REMOVE transition d'état d'entité n'a aucun sens pour une @ManyToManyassociation JPA car elle pourrait déclencher une suppression de chaîne qui effacerait finalement les deux côtés de l'association.
  2. Comme expliqué dans cet article , les méthodes de l'utilitaire d'ajout / suppression sont obligatoires si vous utilisez des associations bidirectionnelles afin que vous puissiez vous assurer que les deux côtés de l'association sont synchronisés.
  3. L' Postentité utilise l'identificateur d'entité pour l'égalité car elle ne dispose d'aucune clé métier unique. Comme expliqué dans cet article , vous pouvez utiliser l'identificateur d'entité pour l'égalité tant que vous vous assurez qu'il reste cohérent dans toutes les transitions d'état d'entité .
  4. L' Tagentité possède une clé métier unique marquée de l' @NaturalIdannotation spécifique à Hibernate . Dans ce cas, la clé métier unique est le meilleur candidat pour les contrôles d'égalité .
  5. L' mappedByattribut de l' postsassociation dans l' Tagentité marque que, dans cette relation bidirectionnelle, l' Postentité est propriétaire de l'association. Cela est nécessaire car un seul côté peut posséder une relation et les modifications ne sont propagées à la base de données que de ce côté particulier.
  6. Il Setest préférable d'utiliser un Listavec @ManyToManyest moins efficace.

Pour plus de détails sur la meilleure façon de cartographier la @ManyToManyrelation avec JPA et Hibernate, consultez cet article .

Vlad Mihalcea
la source
1

cela exigerait probablement une relation plusieurs-à-plusieurs comme suit



public class Person{

    private Long personId;
    @manytomany

    private Set skills;
    //Getters and setters
}

public class Skill{
    private Long skillId;
    private String skillName;
    @manyToMany(MappedBy="skills,targetClass="Person")
    private Set persons; // (people would not be a good convenion)
    //Getters and setters
}

vous devrez peut-être définir un joinTable + JoinColumn mais cela fonctionnera aussi sans ...

msshapira
la source
1

J'expliquerais de cette façon:

OneToOne - Relation OneToOne

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - Relation ManyToOne

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - ManyToMany relation

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;
Duracell De Monaco
la source
0

Tout d'abord, lisez tous les petits caractères. Notez que le mappage relationnel NHibernate (donc, je suppose, Hibernate également) a une correspondance amusante avec le mappage de DB et de graphes d'objets. Par exemple, les relations un à un sont souvent implémentées comme une relation plusieurs à un.

Deuxièmement, avant que nous puissions vous dire comment vous devez écrire votre carte O / R, nous devons également voir votre base de données. En particulier, une seule compétence peut-elle être possédée par plusieurs personnes? Si tel est le cas, vous avez une relation plusieurs-à-plusieurs; sinon, c'est plusieurs à un.

Troisièmement, je préfère ne pas implémenter directement les relations plusieurs-à-plusieurs, mais plutôt modéliser la "table de jointure" dans votre modèle de domaine - c'est-à-dire la traiter comme une entité, comme ceci:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Alors voyez-vous ce que vous avez? Vous avez deux relations un-à-plusieurs. (Dans ce cas, Person peut avoir une collection de PersonSkills, mais pas une collection de Skills.) Cependant, certains préféreront utiliser la relation plusieurs-à-plusieurs (entre Person et Skill); c'est controversé.

Quatrièmement, si vous avez des relations bidirectionnelles (par exemple, non seulement Person a une collection de Compétences, mais aussi, Skill a une collection de Personnes), NHibernate n'applique pas la bidirectionnalité dans votre BL pour vous; il ne comprend la bidirectionnalité des relations qu'à des fins de persistance.

Cinquièmement, plusieurs-à-un est beaucoup plus facile à utiliser correctement dans NHibernate (et je suppose Hibernate) que un-à-plusieurs (mappage de collection).

Bonne chance!

jyoungdev
la source