Różnica między jeden do wielu, wiele do jednego i wiele do wielu?

144

Ok, więc to prawdopodobnie trywialne pytanie, ale mam problem z wizualizacją i zrozumieniem różnic i kiedy ich używać. Nie mam też pewności, w jaki sposób koncepcje takie jak odwzorowania jednokierunkowe i dwukierunkowe wpływają na relacje jeden do wielu / wiele do wielu. Używam teraz Hibernacji, więc wszelkie wyjaśnienia związane z ORM będą pomocne.

Jako przykład załóżmy, że mam następującą konfigurację:

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
}

Więc w tym przypadku jaki rodzaj mapowania miałbym? Odpowiedzi na ten konkretny przykład są zdecydowanie doceniane, ale bardzo chciałbym również omówić, kiedy należy używać opcji jeden do wielu i wiele do wielu, a kiedy używać tabeli łączenia w porównaniu z kolumną łączenia i jednokierunkowej i dwukierunkowej.

Ian Dallas
źródło
11
Wygląda na to, że wszyscy odpowiadają tylko jeden do wielu vs wiele do wielu. Spójrz na moją odpowiedź na jeden do wielu vs wiele do jednego.
Alexander Suraphel,

Odpowiedzi:

165

Jeden do wielu : jedna osoba ma wiele umiejętności, umiejętność nie jest ponownie używana między osobami

  • Jednokierunkowy : osoba może bezpośrednio odwoływać się do Umiejętności poprzez swój Zestaw
  • Dwukierunkowa : każda umiejętność „podrzędna” ma pojedynczy wskaźnik z powrotem do Osoby (który nie jest pokazany w Twoim kodzie)

Wiele do wielu : jedna osoba ma wiele umiejętności, umiejętność jest ponownie używana między osobami

  • Jednokierunkowy : osoba może bezpośrednio odwoływać się do Umiejętności poprzez swój Zestaw
  • Dwukierunkowa : umiejętność ma zestaw osób, które się do niej odnoszą.

W relacji jeden do wielu jeden obiekt jest „rodzicem”, a drugi „dzieckiem”. Rodzic kontroluje istnienie dziecka. W przypadku wielu do wielu istnienie któregokolwiek typu jest zależne od czegoś spoza nich obu (w szerszym kontekście aplikacji).

Twój temat (domena) powinien dyktować, czy relacja jest typu jeden do wielu, czy też wiele do wielu - jednak uważam, że uczynienie relacji jednokierunkowej lub dwukierunkowej jest decyzją inżynieryjną, która ogranicza pamięć, przetwarzanie, wydajność itp.

Co może być mylące, to fakt, że dwukierunkowa relacja wiele do wielu nie musi być symetryczna! Oznacza to, że grupa ludzi może wskazać umiejętność, ale umiejętność nie musi odnosić się tylko do tych osób. Zwykle tak, ale taka symetria nie jest wymagana. Weźmy na przykład miłość - jest dwukierunkowa („I-Love”, „Loves-Me”), ale często asymetryczna („Kocham ją, ale ona mnie nie kocha”)!

Wszystkie są dobrze obsługiwane przez Hibernate i JPA. Pamiętaj tylko, że Hibernate ani żaden inny ORM nie przejmują się utrzymywaniem symetrii podczas zarządzania dwukierunkowymi relacjami wiele do wielu ... to wszystko zależy od aplikacji.

HDave
źródło
Aby wyjaśnić, każda relacja może być jedno- lub dwukierunkowa w twoim BL lub w twoim mapowaniu O / R (nawet niezależnie od siebie!).
jyoungdev
4
Przykład „MIŁOŚĆ” właśnie to wyjaśnił. ManyToMany to mój typ mapowania.
Abdullah Khan
1
Wspaniały. To tak dobrze to wyjaśnia (w kontekście przykładu OP)
Anupam,
1
Nie odpowiada poprawnie na pytanie. tęsknisz za wieloma do jednej części. chociaż jeden do wielu i wiele do jednego to kwestia percepcji, ta odpowiedź nie wspomina o tym.
Manzur Alahi
249

Wygląda na to, że wszyscy odpowiadają One-to-manyvs Many-to-many.:

Różnica pomiędzy One-to-many, Many-to-onea Many-to-Manyto:

One-to-manyvs Many-to-oneto kwestia perspektywy . Unidirectionalvs Bidirectionalnie wpłynie na mapowanie, ale wpłynie na sposób uzyskania dostępu do danych.

  • W Many-to-onetej manystronie będzie zachować odniesienie do oneboku. Dobrym przykładem jest „Państwo ma miasta”. W tym przypadku Statejest to jedna strona i Cityjest wiele stron. W state_idtabeli pojawi się kolumna cities.

W jednokierunkowy , Personklasa będzie mieć List<Skill> skillsale Skillnie będzie miał Person person. W trybie dwukierunkowym obie właściwości są dodawane i umożliwia dostęp do Persondanej umiejętności (tj skill.person.).

  • Z One-to-Manyjednej strony będzie naszym punktem odniesienia. Na przykład „Użytkownik ma adresy”. W tym przypadku możemy mieć trzy kolumny address_1_id, address_2_idi address_3_idalbo spojrzeć w górę tabeli z wyjątkowej presji na user_idi address_id.

W jednokierunkowy , A Userbędzie miał Address address. Dwukierunkowy będzie miał dodatkowe List<User> usersw Addressklasie.

  • W Many-to-Manyczłonkach każdej strony może mieć odniesienie do dowolnej liczby członków drugiej strony. Aby to osiągnąć, używana jest tabela przeglądowa . Przykładem tego są relacje między lekarzami i pacjentami. Lekarz może mieć wielu pacjentów i odwrotnie.
Alexander Suraphel
źródło
27
to powinna być zaakceptowana odpowiedź, większość innych odpowiedzi pomija pytanie.
arg20
Pierwszy przykład jest nieprawidłowy. Jeśli masz osobę, a osoba ma @OneToMany z umiejętnościami, ta tabela preson_skills będzie miała unikalne ograniczenie na skill_id. Więc jedna umiejętność byłaby przypisana tylko do jednej osoby. I nie możesz wypakować s.persons, bo jest tylkos.person
Иван Николайчук
W rzeczywistości One-to-manyrelacja, jaką opisujesz, jest Many-to-manyrelacją, ponieważ personodnosi się do wielu, skillsale skillnie odnosi się do konkretnej osoby, a wiele z nich personsmoże mieć takie samo odniesienie skill. A twój Many-to-onezwiązek jest tak naprawdę, One-to-manyponieważ każda umiejętność ma odniesienie tylko do jednej, personponieważ dziecko ma tylko jedną matkę.
mixel
@mixel Twój komentarz spowodował, że przepisałem większość mojej odpowiedzi. Sprawdź to ponownie! Dzięki
Alexander Suraphel
Rozróżnia się opcje „jeden do wielu” i „wiele do jednego” na podstawie znaczenia strony „jeden”. W obu przykładach „Użytkownik” jest najbardziej znaczącą jednostką. Więc kiedy jest na „Jednej” (użytkownicy i umiejętności, skillsmają person_id), wtedy nazywasz to „Jeden do wielu”, ale kiedy jest to strona „Wiele” (użytkownicy i adresy, usersma address_id), wtedy nazywasz to Wielu do jednego. Ale strukturalnie oba przypadki są identyczne i nazywane jeden-do-wielu.
mixel
38

1) Kręgi to Entities / POJOs / Beans

2) deg jest skrótem od stopnia jak na wykresach (liczba krawędzi)

PK = klucz podstawowy, FK = klucz obcy

Zwróć uwagę na sprzeczność między stopniem a nazwą strony. Wiele odpowiada stopniowi = 1, podczas gdy jeden odpowiada stopniowi> 1.

Ilustracja przedstawiająca jeden do wielu wiele do jednego

jhegedus
źródło
1
Naprawdę podoba mi się sposób, w jaki łączy on wykres obiektowy z tabelami w obu kierunkach.
Dmitry Minkovsky
3
Frajerów wygląd, to jak programisty charakter pisma wygląda następująco: D
Mehraj Malik
Widzę, co tu zrobiłeś.
Nick Gallimore
8

Spójrz na ten artykuł: Mapowanie relacji między obiektami

Istnieją dwie kategorie relacji między obiektami, którymi należy się zająć podczas mapowania. Pierwsza kategoria oparta jest na liczebności i obejmuje trzy typy:

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

Druga kategoria oparta jest na kierunkowości i zawiera dwa typy, relacje jednokierunkowe i relacje dwukierunkowe.

*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
źródło
2
this occurs when the maximum of one multiplicity is one and the other is greater than onelolwut?
serg
3

To bardzo częste pytanie, więc odpowiedź opiera się na artykule, który napisałem na swoim blogu.

Jeden za dużo

Relacja jeden do wielu tabel wygląda następująco:

Jeden za dużo

W systemie relacyjnej bazy danych relacja między tabelami jeden do wielu łączy dwie tabele na podstawie Foreign Keykolumny podrzędnej, która odwołuje się Primary Keydo wiersza tabeli nadrzędnej.

Na powyższym diagramie tabeli post_idkolumna w post_commenttabeli ma Foreign Keyzwiązek z kolumną postidentyfikatora tabeli Primary Key:

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

Adnotacja @ManyToOne

Najlepszym sposobem odwzorowania relacji jeden do wielu w tabeli jest użycie @ManyToOneadnotacji.

W naszym przypadku encja podrzędna PostCommentmapuje post_idkolumnę klucza obcego za pomocą @ManyToOneadnotacji:

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

}

Korzystanie z @OneToManyadnotacji JPA

Tylko dlatego, że masz możliwość korzystania z @OneToManyadnotacji, nie oznacza to, że powinna to być opcja domyślna dla każdego typu „ jeden do wielu” relacji bazie danych. Problem z kolekcjami polega na tym, że możemy ich używać tylko wtedy, gdy liczba rekordów podrzędnych jest raczej ograniczona.

Najlepszym sposobem na zmapowanie @OneToManypowiązania jest poleganie na @ManyToOnestronie przy propagowaniu wszystkich zmian stanu encji:

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

Jednostka nadrzędna Postposiada dwie metody narzędziowe (np. addCommentI removeComment), które są używane do synchronizacji obu stron dwukierunkowego powiązania. Zawsze powinieneś podawać te metody, gdy pracujesz z asocjacją dwukierunkową, ponieważ w przeciwnym razie ryzykujesz bardzo subtelnymi problemami z propagacją stanu .

Należy @OneToManyunikać skojarzenia jednokierunkowego, ponieważ jest mniej wydajne niż użycie @ManyToOnelub @OneToManyskojarzenie dwukierunkowe .

Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @OneToManyrelacji z JPA i Hibernacją, zapoznaj się z tym artykułem .

Jeden na jednego

Relacja jeden do jednego tabeli wygląda następująco:

Jeden na jednego

W systemie relacyjnej bazy danych relacja między tabelami jeden do jednego łączy dwie tabele na podstawie Primary Keykolumny w potomku, która jest również Foreign Keyodwołaniem do Primary Keywiersza tabeli nadrzędnej.

Dlatego możemy powiedzieć, że tabela podrzędna współdzieli Primary Keytabelę nadrzędną.

Na powyższym diagramie tabeli idkolumna w post_detailstabeli ma również Foreign Keyzwiązek z kolumną posttabeli id Primary Key:

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

Używanie JPA @OneToOnez @MapsIdadnotacjami

Najlepszym sposobem odwzorowania @OneToOnerelacji jest użycie @MapsId. W ten sposób nie potrzebujesz nawet powiązania dwukierunkowego, ponieważ zawsze możesz pobrać PostDetailsjednostkę za pomocą Postidentyfikatora jednostki.

Mapowanie wygląda następująco:

[code language = "java"] @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

} [/kod]

W ten sposób idwłaściwość służy zarówno jako klucz podstawowy, jak i klucz obcy. Zauważysz, że @Idkolumna nie używa już @GeneratedValueadnotacji, ponieważ identyfikator jest wypełniony identyfikatorem postpowiązania.

Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @OneToOnerelacji z JPA i Hibernacją, zapoznaj się z tym artykułem .

Wiele do wielu

Relacja między tabelami wiele do wielu wygląda następująco:

Wiele do wielu

W systemie relacyjnej bazy danych relacja wiele-do-wielu tabel łączy dwie tabele nadrzędne za pośrednictwem tabeli podrzędnej, która zawiera dwie Foreign Keykolumny odwołujące się do Primary Keykolumn dwóch tabel nadrzędnych.

Na powyższym schemacie tabeli post_idkolumna w post_tagtabeli ma również Foreign Keyzwiązek z kolumną postid tabeli Primary Key:

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

A tag_idkolumna w post_tagtabeli ma Foreign Keyzwiązek z kolumną tagidentyfikatora tabeli Primary Key:

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

Korzystanie z @ManyToManymapowania JPA

Oto jak można odwzorować many-to-manyrelacje między tabelami za pomocą JPA i 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. tagsStowarzyszenie w Postjednostce określa tylko PERSISTi MERGEkaskadowych typy. Jak wyjaśniono w tym artykule , REMOVE zmiana stanu jednostki nie ma żadnego sensu w przypadku @ManyToManypowiązania JPA, ponieważ może spowodować usunięcie łańcucha, które ostatecznie wymazałoby obie strony skojarzenia.
  2. Jak wyjaśniono w tym artykule , metody narzędziowe dodaj / usuń są obowiązkowe, jeśli używasz skojarzeń dwukierunkowych, aby upewnić się, że obie strony powiązania są zsynchronizowane.
  3. PostJednostka wykorzystuje identyfikator podmiotu, na rzecz równości, ponieważ brakuje żadnego unikalnego klucza biznesowego. Jak wyjaśniono w tym artykule , możesz użyć identyfikatora jednostki dla równości, o ile upewnisz się, że pozostaje on spójny we wszystkich przejściach stanu jednostki .
  4. TagJednostka posiada unikalny klucz przedsiębiorcy, który jest oznaczony Hibernate specyficznych @NaturalIdadnotacji. W takim przypadku unikalny klucz biznesowy jest najlepszym kandydatem do kontroli równości .
  5. mappedByAtrybutem postszrzeszania się w Tagznakach podmiot, który w tym dwukierunkowej relacji, Postjednostka posiada stowarzyszenie. Jest to potrzebne, ponieważ tylko jedna strona może być właścicielem relacji, a zmiany są propagowane do bazy danych tylko z tej konkretnej strony.
  6. Powinien Setbyć preferowany, ponieważ używanie Listwith @ManyToManyjest mniej wydajne.

Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania @ManyToManyrelacji z JPA i Hibernacją, zapoznaj się z tym artykułem .

Vlad Mihalcea
źródło
1

prawdopodobnie wymagałoby to powiązania wielu do wielu, jak następuje



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
}

być może będziesz musiał zdefiniować joinTable + JoinColumn, ale będzie to możliwe również bez ...

msshapira
źródło
1

Tak bym wyjaśnił:

OneToOne - Relacja OneToOne

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - ManyToOne związku

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

Relacja ManyToMany - ManyToMany

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;
Duracell De Monaco
źródło
0

Przede wszystkim przeczytaj wszystko drobnym drukiem. Zauważ, że NHibernate (a więc zakładam, że również Hibernate) mapowanie relacyjne ma zabawny związek z mapowaniem DB i mapowaniem wykresów obiektów. Na przykład relacje jeden do jednego są często implementowane jako relacja wiele do jednego.

Po drugie, zanim będziemy mogli powiedzieć ci, jak powinieneś napisać swoją mapę O / R, musimy również zobaczyć twoją bazę danych. W szczególności, czy jedną Umiejętność może posiadać wiele osób? Jeśli tak, masz relację wiele do wielu; w przeciwnym razie jest wiele do jednego.

Po trzecie, wolę nie implementować bezpośrednio relacji wiele-do-wielu, ale zamiast tego zamodelować „tabelę złączeń” w modelu domeny - tj. Traktuj ją jako jednostkę, na przykład:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Więc widzisz, co masz? Masz dwie relacje jeden do wielu. (W tym przypadku Osoba może mieć zbiór Umiejętności Person, ale nie miałby zbioru Umiejętności). Jednak niektórzy wolą używać relacji wiele do wielu (między Osobą a Umiejętnością); to jest kontrowersyjne.

Po czwarte, jeśli masz relacje dwukierunkowe (np. Nie tylko Osoba ma zbiór Umiejętności, ale także Umiejętność ma kolekcję Osób), NHibernate nie wymusza dwukierunkowości w Twoim BL; rozumie tylko dwukierunkowość relacji dla celów trwałości.

Po piąte, wiele do jednego jest dużo łatwiejsze do poprawnego użycia w NHibernate (i zakładam, że Hibernate) niż jeden do wielu (mapowanie kolekcji).

Powodzenia!

jyoungdev
źródło