Unterschied zwischen Eins-zu-Viele, Viele-zu-Eins und Viele-zu-Viele?

144

Ok, das ist wahrscheinlich eine triviale Frage, aber ich habe Probleme, die Unterschiede zu visualisieren und zu verstehen und wann ich sie verwenden soll. Ich bin mir auch ein wenig unklar, wie sich Konzepte wie unidirektionale und bidirektionale Zuordnungen auf die Eins-zu-Viele / Viele-zu-Viele-Beziehungen auswirken. Ich verwende gerade den Ruhezustand, daher ist jede Erklärung, die sich auf ORM bezieht, hilfreich.

Nehmen wir als Beispiel an, ich habe das folgende Setup:

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
}

Welche Art von Zuordnung hätte ich in diesem Fall? Antworten auf dieses spezielle Beispiel sind auf jeden Fall willkommen, aber ich möchte auch einen Überblick darüber, wann entweder eins zu viele und viele zu viele verwendet werden sollen und wann eine Verknüpfungstabelle gegenüber einer Verknüpfungsspalte und unidirektional gegenüber bidirektional verwendet werden soll.

Ian Dallas
quelle
11
Es sieht so aus, als würde jeder nur Eins-zu-Viele gegen Viele-zu-Viele antworten. Schauen Sie sich meine Antwort für Eins-zu-Viele gegen Viele-zu-Eins an.
Alexander Suraphel

Antworten:

165

Eins-zu-viele : Eine Person hat viele Fähigkeiten, eine Fähigkeit wird zwischen Personen nicht wiederverwendet.

  • Unidirektional : Eine Person kann über ihr Set direkt auf Fähigkeiten verweisen
  • Bidirektional : Jede "untergeordnete" Fähigkeit hat einen einzelnen Zeiger auf die Person (der in Ihrem Code nicht angezeigt wird).

Viele-zu-Viele : Eine Person hat viele Fähigkeiten, eine Fähigkeit wird zwischen Personen wiederverwendet.

  • Unidirektional : Eine Person kann über ihr Set direkt auf Fähigkeiten verweisen
  • Bidirektional : Eine Fertigkeit hat eine Reihe von Personen, die sich darauf beziehen.

In einer Eins-zu-Viele-Beziehung ist ein Objekt das "Elternteil" und eines das "Kind". Der Elternteil kontrolliert die Existenz des Kindes. In einem Many-To-Many hängt die Existenz eines der beiden Typen von etwas außerhalb der beiden ab (im größeren Anwendungskontext).

Ihr Thema (Domäne) sollte bestimmen, ob die Beziehung Eins-zu-Viele oder Viele-zu-Viele ist oder nicht. Ich finde jedoch, dass es eine technische Entscheidung ist, die Beziehung unidirektional oder bidirektional zu machen, die Speicher, Verarbeitung und Leistung beeinträchtigt , etc.

Was verwirrend sein kann, ist, dass eine bidirektionale Viele-zu-Viele-Beziehung nicht symmetrisch sein muss! Das heißt, eine Gruppe von Menschen könnte auf eine Fertigkeit hinweisen, aber die Fertigkeit muss sich nicht nur auf diese Personen beziehen. Typischerweise würde es, aber eine solche Symmetrie ist keine Voraussetzung. Nehmen wir zum Beispiel die Liebe - sie ist bidirektional ("Ich-Liebe", "Liebt-mich"), aber oft asymmetrisch ("Ich liebe sie, aber sie liebt mich nicht")!

All dies wird von Hibernate und JPA gut unterstützt. Denken Sie daran, dass der Ruhezustand oder ein anderes ORM keine Rolle bei der Aufrechterhaltung der Symmetrie spielt, wenn bidirektionale Viele-zu-Viele-Beziehungen verwaltet werden. Das hängt alles von der Anwendung ab.

HDave
quelle
Zur Verdeutlichung kann jede Beziehung in Ihrem BL oder in Ihrer O / R-Zuordnung uni- oder bidirektional sein (sogar unabhängig voneinander!).
Jyoungdev
4
Das Beispiel "LIEBE" hat es nur klargestellt. ManyToMany ist meine Art der Zuordnung.
Abdullah Khan
1
Super. Dies erklärt es so gut (und im Zusammenhang mit dem Beispiel von OP)
Anupam
1
Beantwortet die Frage nicht richtig. Sie vermissen die vielen zu einem Teil. Obwohl eins zu viele und viele zu eins eine Frage der Wahrnehmung ist, erwähnt diese Antwort das nicht.
Manzur Alahi
248

Sieht aus wie jeder antwortet One-to-manyvs. Many-to-many:

Der Unterschied zwischen One-to-many, Many-to-oneund Many-to-Manyist:

One-to-manyvs Many-to-oneist eine Frage der Perspektive . Unidirectionalvs Bidirectionalhat keinen Einfluss auf die Zuordnung, macht jedoch einen Unterschied darin, wie Sie auf Ihre Daten zugreifen können.

  • In Many-to-oneder manySeite wird die Referenz der oneSeite behalten . Ein gutes Beispiel ist "Ein Staat hat Städte". In diesem Fall Stateist die eine Seite und Cityist die viele Seite. state_idIn der Tabelle wird eine Spalte angezeigt cities.

In unidirektionaler Richtung wird die PersonKlasse haben, List<Skill> skillsaber Skillnicht haben Person person. In bidirektional werden beide Eigenschaften hinzugefügt und Sie können auf eine Personbestimmte Fähigkeit (dh skill.person) zugreifen .

  • Auf One-to-Manyder einen Seite wird unser Bezugspunkt sein. Zum Beispiel "Ein Benutzer hat eine Adresse". In diesem Fall könnten wir drei Spalten haben address_1_id, address_2_idund address_3_idoder eine Nachschlagtabelle mit UNIQUE - Einschränkung auf user_idund address_id.

In unidirektionaler Richtung wird ein UserWille haben Address address. Bidirektional wird eine zusätzliche List<User> usersin der AddressKlasse haben.

  • In Many-to-ManyMitgliedern jeder Partei kann auf eine beliebige Anzahl von Mitgliedern der anderen Partei Bezug genommen werden. Um dies zu erreichen, wird eine Nachschlagetabelle verwendet. Ein Beispiel dafür ist die Beziehung zwischen Ärzten und Patienten. Ein Arzt kann viele Patienten haben und umgekehrt.
Alexander Suraphel
quelle
27
Dies sollte die akzeptierte Antwort sein, die meisten anderen Antworten verfehlen die Frage.
arg20
Das erste Beispiel ist falsch. Wenn Sie eine Person haben und Person @OneToMany mit Fähigkeiten hat, hat diese Tabelle preson_skills eine eindeutige Einschränkung für Skill_id. Eine Fertigkeit würde also nur einer Person zugeordnet. Und Sie können nicht extrahieren s.persons, da es nur gibts.person
4ван Николайчук
Tatsächlich ist eine One-to-manyBeziehung, wie Sie sie beschreiben, eine Many-to-manyBeziehung, da sie personsich auf viele bezieht skills, sich skilljedoch nicht auf eine bestimmte Person bezieht und viele personseine gleiche Referenz haben können skill. Und Ihre Many-to-oneBeziehung besteht tatsächlich darin, One-to-manydass jede Fähigkeit nur einen Bezug hat, personda ein Kind nur eine Mutter hat.
Mixel
@mixel Ihr Kommentar hat mich dazu gebracht, den größten Teil meiner Antwort neu zu schreiben. Bitte probieren Sie es noch einmal aus! Danke
Alexander Suraphel
Sie unterscheiden zwischen Eins-zu-Viele und Viele-zu-Eins durch die Bedeutung der "Eins" -Seite. In beiden Beispielen ist "Benutzer" die wichtigste Entität. Wenn es also auf der "Eins" ist (Benutzer und Fähigkeiten, skillshat person_id), dann nennt man es Eins-zu-Viele, aber wenn es eine ist, die "Viele" Seite (Benutzer und Adressen, usershat address_id), dann nennt man es Viele-zu-Eins. Aber strukturell sind beide Fälle identisch und werden Eins-zu-Viele genannt.
Mixel
38

1) Die Kreise sind Entities / POJOs / Beans

2) Grad ist eine Abkürzung für Grad wie in Diagrammen (Anzahl der Kanten)

PK = Primärschlüssel, FK = Fremdschlüssel

Beachten Sie den Widerspruch zwischen dem Grad und dem Namen der Seite. Viele entsprechen Grad = 1, während Eins Grad> 1 entspricht.

Illustration von eins zu viele viele zu eins

jhegedus
quelle
1
Ich liebe es wirklich, wie es den Objektgraphen in beide Richtungen mit den Tabellen verbindet.
Dmitry Minkovsky
3
Schauen Sie Nerds, so sieht PROGRAMMERS HANDSCHRIFT aus: D
Mehraj Malik
Ich sehe, was du hier gemacht hast.
Nick Gallimore
8

Schauen Sie sich diesen Artikel an: Zuordnen von Objektbeziehungen

Es gibt zwei Kategorien von Objektbeziehungen, mit denen Sie sich beim Mapping befassen müssen. Die erste Kategorie basiert auf der Vielfalt und umfasst drei Typen:

*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. 

Die zweite Kategorie basiert auf Direktionalität und enthält zwei Typen, unidirektionale Beziehungen und bidirektionale Beziehungen.

*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
quelle
2
this occurs when the maximum of one multiplicity is one and the other is greater than onelol was?
Serg
3

Dies ist eine sehr häufige Frage, daher basiert diese Antwort auf diesem Artikel, den ich in meinem Blog geschrieben habe.

Eins zu viele

Die Eins-zu-Viele-Tabellenbeziehung sieht wie folgt aus:

Eins zu viele

In einem relationalen Datenbanksystem verknüpft eine Eins-zu-Viele-Tabellenbeziehung zwei Tabellen basierend auf einer Foreign KeySpalte im untergeordneten Element, die Primary Keyauf die der übergeordneten Tabellenzeile verweist .

Im obigen Tabellendiagramm hat die post_idSpalte in der post_commentTabelle eine Foreign KeyBeziehung zur Spalte mit der postTabellen-ID Primary Key:

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

@ManyToOne Annotation

Der beste Weg, um die Eins-zu-Viele-Tabellenbeziehung abzubilden, ist die Verwendung der @ManyToOneAnmerkung.

In unserem Fall PostCommentordnet die post_iduntergeordnete Entität die Spalte @ManyToOneFremdschlüssel mithilfe der Anmerkung zu:

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

}

Verwenden der JPA- @OneToManyAnnotation

Nur weil Sie die Option haben, die @OneToManyAnmerkung zu verwenden, bedeutet dies nicht, dass dies die Standardoption für jede Eins-zu-Viele- Datenbankbeziehung sein sollte. Das Problem bei Sammlungen ist, dass wir sie nur verwenden können, wenn die Anzahl der untergeordneten Datensätze eher begrenzt ist.

Der beste Weg, eine @OneToManyZuordnung zuzuordnen , besteht darin, sich auf die @ManyToOneSeite zu verlassen, um alle Änderungen des Entitätsstatus zu verbreiten:

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

Die übergeordnete Entität verfügt Postüber zwei Dienstprogrammmethoden (z. B. addCommentund removeComment), mit denen beide Seiten der bidirektionalen Zuordnung synchronisiert werden. Sie sollten diese Methoden immer bereitstellen, wenn Sie mit einer bidirektionalen Zuordnung arbeiten, da Sie sonst sehr subtile Probleme bei der Zustandsausbreitung riskieren .

Die unidirektionale @OneToManyZuordnung ist zu vermeiden, da sie weniger effizient ist als die Verwendung @ManyToOneoder die bidirektionale @OneToManyZuordnung.

Weitere Informationen zur besten Zuordnung der @OneToManyBeziehung zu JPA und Hibernate finden Sie in diesem Artikel .

Eins zu eins

Die Eins-zu-Eins-Tabellenbeziehung sieht wie folgt aus:

Eins zu eins

In einem relationalen Datenbanksystem verknüpft eine Eins-zu-Eins-Tabellenbeziehung zwei Tabellen basierend auf einer Primary KeySpalte im untergeordneten Foreign KeyElement, die auch Primary Keyauf die übergeordnete Tabellenzeile verweist .

Daher können wir sagen, dass die untergeordnete Tabelle die Primary Keymit der übergeordneten Tabelle teilt .

Im obigen Tabellendiagramm hat die idSpalte in der post_detailsTabelle auch eine Foreign KeyBeziehung zur postTabellenspalte id Primary Key:

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

Verwenden der JPA @OneToOnemit @MapsIdAnmerkungen

Der beste Weg, eine @OneToOneBeziehung abzubilden, ist die Verwendung @MapsId. Auf diese Weise benötigen Sie nicht einmal eine bidirektionale Zuordnung, da Sie die PostDetailsEntität jederzeit mithilfe der PostEntitätskennung abrufen können.

Das Mapping sieht folgendermaßen aus:

[code language = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") öffentliche Klasse 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]

Auf diese Weise iddient die Eigenschaft sowohl als Primärschlüssel als auch als Fremdschlüssel. Sie werden feststellen, dass in der @IdSpalte keine @GeneratedValueAnmerkung mehr verwendet wird, da der Bezeichner mit dem Bezeichner der postZuordnung gefüllt ist .

Weitere Informationen zur besten Zuordnung der @OneToOneBeziehung zu JPA und Hibernate finden Sie in diesem Artikel .

Viel zu viel

Die Viele-zu-Viele-Tabellenbeziehung sieht wie folgt aus:

Viel zu viel

In einem relationalen Datenbanksystem verknüpft eine Viele-zu-Viele-Tabellenbeziehung zwei übergeordnete Tabellen über eine untergeordnete Tabelle, die zwei Foreign KeySpalten enthält , die auf die Primary KeySpalten der beiden übergeordneten Tabellen verweisen .

Im obigen Tabellendiagramm hat die post_idSpalte in der post_tagTabelle auch eine Foreign KeyBeziehung zur postTabellen-ID- Primary KeySpalte:

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

Die tag_idSpalte in der post_tagTabelle hat eine Foreign KeyBeziehung zur Spalte mit der tagTabellen-ID Primary Key:

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

Verwenden der JPA- @ManyToManyZuordnung

So können Sie die many-to-manyTabellenbeziehung mit JPA und Hibernate zuordnen:

@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. Die tagsZuordnung in der PostEntität definiert nur die Typen PERSISTund MERGEKaskadentypen. Wie in diesem Artikel erläutert , ist der REMOVE Entitätsstatusübergang für eine @ManyToManyJPA-Zuordnung nicht sinnvoll, da er eine Kettenlöschung auslösen könnte, die letztendlich beide Seiten der Zuordnung löschen würde.
  2. Wie in diesem Artikel erläutert , sind die Dienstprogramme zum Hinzufügen / Entfernen obligatorisch, wenn Sie bidirektionale Zuordnungen verwenden, damit Sie sicherstellen können, dass beide Seiten der Zuordnung synchron sind.
  3. Die PostEntität verwendet die Entitätskennung für die Gleichheit, da ihr ein eindeutiger Geschäftsschlüssel fehlt. Wie in diesem Artikel erläutert , können Sie die Entitätskennung für die Gleichheit verwenden, solange Sie sicherstellen, dass sie über alle Entitätsstatusübergänge hinweg konsistent bleibt .
  4. Die TagEntität verfügt über einen eindeutigen Geschäftsschlüssel, der mit der @NaturalIdAnnotation für den Ruhezustand gekennzeichnet ist . In diesem Fall ist der eindeutige Geschäftsschlüssel der beste Kandidat für Gleichstellungsprüfungen .
  5. Das mappedByAttribut der postsZuordnung in der TagEntität kennzeichnet, dass in dieser bidirektionalen Beziehung die PostEntität die Zuordnung besitzt. Dies ist erforderlich, da nur eine Seite eine Beziehung besitzen kann und Änderungen nur von dieser bestimmten Seite an die Datenbank weitergegeben werden.
  6. Das Setist zu bevorzugen, da die Verwendung eines Listwith @ManyToManyweniger effizient ist.

Weitere Informationen zur besten Zuordnung der @ManyToManyBeziehung zu JPA und Hibernate finden Sie in diesem Artikel .

Vlad Mihalcea
quelle
1

Dies würde wahrscheinlich ein Viele-zu-Viele-Beziehungsschiff wie folgt erfordern



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
}

Möglicherweise müssen Sie eine joinTable + JoinColumn definieren, aber es funktioniert auch ohne ...

msshapira
quelle
1

Ich würde das so erklären:

OneToOne - OneToOne- Beziehung

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - ManyToOne- Beziehung

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - ManyToMany- Beziehung

@ManyToMany
List<Traveler> travelers;

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

Lesen Sie zunächst das Kleingedruckte. Beachten Sie, dass die relationale Zuordnung von NHibernate (also vermutlich auch von Hibernate) eine lustige Entsprechung zur Zuordnung von DB und Objektgraphen aufweist. Beispielsweise werden Eins-zu-Eins-Beziehungen häufig als Viele-zu-Eins-Beziehung implementiert.

Zweitens müssen wir auch Ihre Datenbank sehen, bevor wir Ihnen sagen können, wie Sie Ihre O / R-Karte schreiben sollen. Kann eine einzelne Fähigkeit insbesondere von mehreren Personen besessen werden? Wenn ja, haben Sie eine Viele-zu-Viele-Beziehung; Ansonsten ist es viele zu eins.

Drittens ziehe ich es vor, nicht viele-zu-viele-Beziehungen direkt zu implementieren, sondern stattdessen die "Join-Tabelle" in Ihrem Domänenmodell zu modellieren - dh sie als eine Entität wie folgt zu behandeln:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Dann siehst du was du hast? Sie haben zwei Eins-zu-Viele-Beziehungen. (In diesem Fall verfügt die Person möglicherweise über eine Sammlung von PersonSkills, jedoch nicht über eine Sammlung von Skills.) Einige bevorzugen jedoch die Verwendung einer Viele-zu-Viele-Beziehung (zwischen Person und Fähigkeit). das ist umstritten.

Viertens, wenn Sie bidirektionale Beziehungen zu tun haben (zB nicht nur , dass Person , die eine Sammlung von Fähigkeiten, sondern auch, Fähigkeit hat eine Sammlung von Personen), ist NHibernate nicht Bidirektionalität in Ihrem BL für Sie erzwingen; Es versteht nur die Bidirektionalität der Beziehungen für Persistenzzwecke.

Fünftens ist Many-to-One in NHibernate (und ich gehe von Hibernate aus) viel einfacher korrekt zu verwenden als One-to-Many (Sammlungszuordnung).

Viel Glück!

jyoungdev
quelle