215 votes

Différence entre One-to-Many, Many-to-One et Many-to-Many ?

Il s'agit probablement d'une question triviale, mais j'ai du mal à visualiser et à comprendre les différences et à savoir quand utiliser chacune d'entre elles. Je ne sais pas non plus comment des concepts tels que les mappings unidirectionnels et bidirectionnels affectent les relations one-to-many/many-to-many. J'utilise Hibernate en ce moment, donc toute explication liée à l'ORM sera utile.

A 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
}

Dans ce cas, quel type de cartographie devrais-je avoir ? Les réponses à cet exemple spécifique seront certainement appréciées, mais j'aimerais aussi avoir une vue d'ensemble des cas où l'on peut utiliser soit one-to-many, soit many-to-many, et où l'on peut utiliser une table de jointure plutôt qu'une colonne de jointure, et unidirectionnel plutôt que bidirectionnel.

386voto

Il semble que tout le monde réponde One-to-many vs. Many-to-many :

La différence entre One-to-many , Many-to-one y Many-to-Many est :

One-to-many vs Many-to-one est une question de perspective . Unidirectional vs Bidirectional n'affectera pas la cartographie mais fera une différence sur la façon dont vous pouvez accéder à vos données.

  • En Many-to-one les many gardera la référence de la one côté. Un bon exemple est "Un État a des villes". Dans ce cas State est le côté unique et City est le côté multiple. Il y aura une colonne state_id dans le tableau cities .

En unidirectionnel , Person la classe aura List<Skill> skills mais Skill n'aura pas Person person . En bidirectionnel , les deux sont ajoutées et vous permettent d'accéder à un Person étant donné qu'un compétence( i.e. skill.person ).

En unidirectionnel , a User aura Address address . Bidirectionnel disposera d'un montant supplémentaire de List<User> users dans le Address classe.

  • En Many-to-Many Les membres de chaque partie peuvent se référer à un nombre arbitraire de membres de l'autre partie. Pour ce faire, un tableau de consultation est utilisé. La relation entre les médecins et les patients en est un exemple. Un médecin peut avoir de nombreux patients et vice versa.

216voto

HDave Points 6943

Un à plusieurs : Une personne a plusieurs compétences, une compétence n'est pas réutilisée d'une personne à l'autre

  • Unidirectionnel : Une personne peut faire directement référence à des compétences par l'intermédiaire de son ensemble
  • Bidirectionnel : Chaque compétence "enfant" dispose d'un pointeur unique vers la compétence Personne (qui n'apparaît pas dans votre code)

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

  • Unidirectionnel : Une personne peut faire directement référence à des compétences par l'intermédiaire de son ensemble
  • Bidirectionnel : Une compétence a un ensemble de personne(s) qui s'y rapporte(nt).

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 une relation de plusieurs à plusieurs, l'existence de l'un ou l'autre type d'objet dépend d'un élément extérieur aux deux (dans le contexte d'une application plus large).

Votre sujet (domaine) devrait dicter si la relation est de type un à plusieurs ou de type plusieurs à plusieurs. Cependant, je trouve que rendre la relation unidirectionnelle ou bidirectionnelle est une décision d'ingénierie qui fait des compromis entre la mémoire, le traitement, les performances, etc.

Ce qui peut prêter à confusion, c'est qu'une relation bidirectionnelle Many-To-Many n'a pas besoin d'être symétrique ! En d'autres termes, un groupe de personnes peut pointer vers une compétence, mais la compétence ne doit pas nécessairement se rapporter à ces seules personnes. C'est généralement le cas, mais cette symétrie n'est pas obligatoire. Prenons l'exemple de l'amour : il est bidirectionnel ("J'aime", "Je m'aime"), 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 préoccupe pas de maintenir la symétrie lors de la gestion de relations bidirectionnelles de plusieurs à plusieurs... c'est l'application qui s'en charge.

48voto

jhegedus Points 1314

1) Les cercles sont des Entités/POJOs/Beans

2) deg est l'abréviation de degree comme dans les graphes (nombre d'arêtes)

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

Notez la contradiction entre le diplôme et le nom du camp. Beaucoup correspond à un degré=1 alors que Un correspond à un degré >1.

Illustration of one-to-many many-to-one

42voto

Vlad Mihalcea Points 3628

Un à plusieurs

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

One-to-many

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

Dans le diagramme du tableau ci-dessus, le post_id dans la colonne post_comment Le tableau a une Foreign Key la relation avec le post table id Primary Key colonne :

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

Annotation @ManyToOne

En JPA, la meilleure façon de représenter la relation de table un-à-plusieurs est d'utiliser l'attribut @ManyToOne annotation.

Dans notre cas, le PostComment met en correspondance l'entité enfant post_id colonne de clé étrangère à l'aide de l'option @ManyToOne annotation :

    @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 de l'APP @OneToMany annotation

Ce n'est pas parce que vous avez la possibilité d'utiliser le @OneToMany cela ne signifie pas qu'elle doit être l'option par défaut pour toutes les annotations de l un à plusieurs les relations entre les bases de données.

Le problème des collections JPA est qu'elles ne peuvent être utilisées que lorsque le nombre d'éléments est assez faible.

La meilleure façon de cartographier un @OneToMany est de s'appuyer sur la @ManyToOne pour propager tous les changements d'état de l'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);
        }
    }

Le parent Post comporte deux méthodes utilitaires (par exemple addComment y removeComment ) qui sont utilisés pour synchroniser les deux côtés de l'association bidirectionnelle.

Vous devez fournir ces méthodes chaque fois que vous travaillez avec une association bidirectionnelle. problèmes très subtils de propagation de l'état .

Le système unidirectionnel @OneToMany est à éviter car elle est moins efficace que l'utilisation de l'association @ManyToOne ou l'option bidirectionnelle @OneToMany association.

Un à un

La relation univoque entre les tables se présente comme suit :

One-to-one

Dans un système de base de données relationnelle, une relation de table univoque relie deux tables sur la base d'une Primary Key dans l'enfant qui est également un Foreign Key renvoyant à la Primary Key de la ligne du tableau parent.

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

Dans le diagramme du tableau ci-dessus, le id dans la colonne post_details a également un Foreign Key la relation avec le post table id Primary Key colonne :

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

Utilisation de l'APP @OneToOne avec @MapsId annotations

La meilleure façon de cartographier un @OneToOne 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 le fichier PostDetails en utilisant l'option Post identifiant de l'entité.

La cartographie se présente comme suit :

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class 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
}

De cette manière, le id sert à la fois de clé primaire et de clé étrangère. Vous remarquerez que la propriété @Id n'utilise plus de colonne @GeneratedValue puisque l'identifiant est complété par l'identifiant de l'élément post l'association.

Plusieurs à plusieurs

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

Many-to-many

Dans un système de base de données relationnelle, une relation de table de plusieurs à plusieurs relie deux tables mères par l'intermédiaire d'une table fille qui contient deux tables. Foreign Key les colonnes qui font référence à la Primary Key des deux tables mères.

Dans le diagramme du tableau ci-dessus, le post_id dans la colonne post_tag a également un Foreign Key la relation avec le post table id Primary Key colonne :

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

Et, le tag_id dans la colonne post_tag Le tableau a une Foreign Key la relation avec le tag table id Primary Key colonne :

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

Utilisation de l'APP @ManyToMany cartographie

C'est ainsi que vous pouvez mapper le many-to-many 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 getClass().hashCode();
        }
    }

    @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. Les tags dans l'association Post ne définit que l'entité PERSIST y MERGE les types de cascades. Les REMOVE La transition d'état d'une entité n'a pas de sens pour un @ManyToMany JPA, car cela pourrait déclencher une suppression de chaîne qui finirait par effacer les deux parties de l'association.
  2. Les méthodes utilitaires d'ajout/suppression sont obligatoires si vous utilisez des associations bidirectionnelles afin de vous assurer que les deux parties de l'association sont synchronisées.
  3. Les Post utilise l'identifiant de l'entité à des fins d'égalité, car elle ne dispose pas d'une clé de gestion unique. Vous pouvez utiliser l'identifiant de l'entité pour l'égalité à condition de vous assurer qu'il reste cohérent dans toutes les transitions d'état de l'entité.
  4. Les Tag possède une clé de gestion unique qui est marquée par la clé de gestion spécifique à Hibernate @NaturalId annotation. Lorsque c'est le cas, la clé unique de l'entreprise est le meilleur candidat pour les contrôles d'égalité .
  5. Les mappedBy de l'attribut posts dans l'association Tag marque que, dans cette relation bidirectionnelle, l'entité Post est propriétaire de l'association. Cela est nécessaire car une relation ne peut appartenir qu'à une seule partie et les modifications ne sont propagées dans la base de données qu'à partir de cette partie particulière.
  6. Les Set est à privilégier, car l'utilisation d'un List avec @ManyToMany est moins efficace.

12voto

Duracell De Monaco Points 1146

C'est ainsi que je l'expliquerais :

OneToOne - OneToOne relation

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - ManyToOne relation

@OneToMany
Shepherd shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - ManyToMany relation

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X