Differenza tra uno-a-molti, molti-a-uno e molti-a-molti?


144

Ok, questa è probabilmente una domanda banale ma ho problemi a visualizzare e capire le differenze e quando usarle. Sono anche un po 'poco chiaro su come concetti come i mapping unidirezionali e bidirezionali influenzano le relazioni uno-a-molti / molti-a-molti. Sto usando Hibernate in questo momento, quindi qualsiasi spiegazione relativa all'ORM sarà utile.

Ad esempio diciamo che ho il seguente set-up:

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
}

Quindi in questo caso che tipo di mappatura dovrei avere? Le risposte a questo esempio specifico sono sicuramente apprezzate, ma vorrei anche una panoramica di quando usare uno-a-molti e molti-a-molti e quando usare una tabella di join rispetto a una colonna di join e unidirezionale contro bidirezionale.


11
Sembra che tutti stiano rispondendo solo Uno-a-molti vs Molti-a-molti. Guarda la mia risposta per One-to-many vs Many-to-one.
Alexander Suraphel,

Risposte:


165

Uno-a-molti : una persona ha molte abilità, un'abilità non viene riutilizzata tra le persone

  • Unidirezionale : una persona può fare direttamente riferimento alle abilità tramite il suo set
  • Bidirezionale : ogni abilità "figlio" ha un singolo puntatore di backup sulla persona (che non è mostrata nel tuo codice)

Many-to-Many : una persona ha molte abilità, un'abilità viene riutilizzata tra le persone

  • Unidirezionale : una persona può fare direttamente riferimento alle abilità tramite il suo set
  • Bidirezionale : un'abilità ha una serie di persone che la riguardano.

In una relazione uno-a-molti, un oggetto è il "genitore" e uno è il "figlio". Il genitore controlla l'esistenza del bambino. In Many-To-Many, l'esistenza di entrambi i tipi dipende da qualcosa al di fuori di entrambi (nel contesto più ampio dell'applicazione).

Il tuo argomento (dominio) dovrebbe dettare se la relazione è uno-a-molti o molti-a-molti - tuttavia, trovo che rendere la relazione unidirezionale o bidirezionale sia una decisione ingegneristica che scambia memoria, elaborazione, prestazioni , eccetera.

Ciò che può confondere è che una relazione bidirezionale Many-To-Many non deve essere simmetrica! Cioè, un gruppo di persone potrebbe indicare un'abilità, ma l'abilità non deve essere correlata solo a quelle persone. In genere lo sarebbe, ma tale simmetria non è un requisito. Prendi l'amore, ad esempio - è bidirezionale ("I-Love", "Loves-Me"), ma spesso asimmetrico ("La amo, ma lei non mi ama")!

Tutti questi sono ben supportati da Hibernate e JPA. Ricorda solo che Hibernate o qualsiasi altro ORM non si preoccupano di mantenere la simmetria quando si gestiscono relazioni bidirezionali bidirezionali ... tutto dipende dall'applicazione.


Per chiarire, qualsiasi relazione può essere unidirezionale o bidirezionale nel BL o nella mappatura O / R (indipendentemente l'una dall'altra, anche!).
jyoungdev,

4
L'esempio "LOVE" lo ha appena chiarito. ManyToMany è il mio tipo di mappatura.
Abdullah Khan,

1
Super. Questo lo spiega così bene (e nel contesto dell'esempio di OP)
Anupam

1
Non risponde correttamente alla domanda. ti mancano le molte in una parte. sebbene uno a molti e molti a uno sia una questione di percezione, questa risposta non lo menziona.
Manzur Alahi,

248

Sembra che tutti sta rispondendo One-to-manyvs. Many-to-many:

La differenza tra One-to-many, Many-to-oneed Many-to-Manyè:

One-to-manyvs Many-to-oneè una questione di prospettiva . Unidirectionalvs Bidirectionalnon influirà sulla mappatura ma farà la differenza su come è possibile accedere ai dati.

  • Many-to-oneNel manylato manterrà riferimento del onelato. Un buon esempio è "Uno stato ha città". In questo caso Stateè il lato ed Cityè il lato molti. Ci sarà una colonna state_idnella tabella cities.

In unidirezionale , la Personclasse avrà List<Skill> skillsma Skillnon avrà Person person. In bidirezionale , entrambe le proprietà vengono aggiunte e ti consente di accedere a una Persondeterminata abilità (ad es skill.person.).

  • Da One-to-Manyun lato ci sarà il nostro punto di riferimento. Ad esempio, "Un utente ha un indirizzo". In questo caso potremmo avere tre colonne address_1_id, address_2_ide address_3_idoppure una tabella di ricerca con il vincolo univoco user_ide address_id.

In unidirezionale , un Useravrà Address address. Bidirezionale avrà un ulteriore List<User> usersnella Addressclasse.

  • Nei Many-to-Manymembri di ciascuna parte può essere indicato un numero arbitrario di membri dell'altra parte. Per raggiungere questo obiettivo viene utilizzata una tabella di ricerca . Esempio per questo è il rapporto tra medici e pazienti. Un medico può avere molti pazienti e viceversa.

27
questa dovrebbe essere la risposta accettata, la maggior parte delle altre risposte manca la domanda.
arg20,

Il primo esempio non è corretto. Se hai una persona e una persona ha @OneToMany con abilità, quella tabella preson_skills avrebbe un vincolo unico su skill_id. Quindi un'abilità verrebbe mappata solo a una persona. E non puoi estrarre s.persons, dato che c'è solos.person
Иван Николайчук

In realtà la One-to-manyrelazione come descrivi è Many-to-manyrelazione perché personha un riferimento a molti skillsma skillnon mantiene il riferimento a una persona particolare e molti personspossono avere un riferimento uguale skill. E la tua Many-to-onerelazione è in realtà One-to-manyperché ogni abilità ha un riferimento solo a una personcome un bambino ha una sola madre.
mixel

@mixel il tuo commento ha finito per farmi riscrivere la maggior parte della mia risposta. Per favore, controlla di nuovo! Grazie
Alexander Suraphel

Si distingue tra Uno-a-molti e Molti-a-uno dal significato del lato "Uno". In entrambi gli esempi "Utente" è l'entità più significativa. Quindi quando è su "Uno" (utenti e competenze, skillsha person_id) allora lo chiami Uno-a-molti ma quando è uno il lato "Molti" (utenti e indirizzi, usersha address_id) allora lo chiami Molti-a-uno. Ma strutturalmente entrambi i casi sono identici e chiamati uno-a-molti.
Mixel

38

1) I cerchi sono Entità / POJO / Fagioli

2) deg è un'abbreviazione per grado come nei grafici (numero di spigoli)

PK = Chiave primaria, FK = Chiave esterna

Nota la contraddizione tra il grado e il nome del lato. Molti corrispondono a grado = 1 mentre Uno corrisponde a grado> 1.

Illustrazione di uno a molti molti a uno


1
Adoro davvero come lega il grafico degli oggetti alle tabelle in entrambe le direzioni.
Dmitry Minkovsky,

3
Nerds Guarda, questo è il modo di programmazione della scrittura assomiglia a: D
Mehraj Malik

Vedo cosa hai fatto qui.
Nick Gallimore,

8

Dai un'occhiata a questo articolo: Mappatura delle relazioni oggetto

Esistono due categorie di relazioni oggetto che è necessario considerare durante la mappatura. La prima categoria si basa sulla molteplicità e comprende tre tipi:

*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 seconda categoria si basa sulla direzionalità e contiene due tipi, relazioni unidirezionali e relazioni bidirezionali.

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

2
this occurs when the maximum of one multiplicity is one and the other is greater than onelolwut?
serg

3

Questa è una domanda molto comune, quindi questa risposta si basa su questo articolo che ho scritto sul mio blog.

Uno-a-molti

La relazione della tabella uno-a-molti ha il seguente aspetto:

Uno-a-molti

In un sistema di database relazionale, una relazione da una a molte tabelle collega due tabelle basate su una Foreign Keycolonna nel figlio che fa riferimento alla Primary Keyriga della tabella padre.

Nel diagramma della tabella sopra, la post_idcolonna nella post_commenttabella ha una Foreign Keyrelazione con la colonna postID tabella Primary Key:

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

@ManyToOne annotation

Il modo migliore per mappare la relazione della tabella uno-a-molti è utilizzare l' @ManyToOneannotazione.

Nel nostro caso, l'entità figlio, PostCommentmappa la post_idcolonna Chiave esterna usando l' @ManyToOneannotazione:

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

}

Utilizzo @OneToManydell'annotazione JPA

Solo perché hai la possibilità di utilizzare l' @OneToManyannotazione, ciò non significa che questa dovrebbe essere l'opzione predefinita per ogni relazione di database uno-a-molti . Il problema con le raccolte è che possiamo usarle solo quando il numero di record figlio è piuttosto limitato.

Il modo migliore per mappare @OneToManyun'associazione è fare affidamento sul @ManyToOnelato per propagare tutte le modifiche allo stato dell'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à padre Postpresenta due metodi di utilità (ad es. addCommentE removeComment) che vengono utilizzati per sincronizzare entrambi i lati dell'associazione bidirezionale. Dovresti sempre fornire questi metodi ogni volta che lavori con un'associazione bidirezionale poiché, altrimenti, rischi di presentare problemi di propagazione dello stato molto sottili .

L' @OneToManyassociazione unidirezionale deve essere evitata in quanto è meno efficiente dell'utilizzo @ManyToOneo @OneToManydell'associazione bidirezionale .

Per ulteriori dettagli sul modo migliore per mappare la @OneToManyrelazione con JPA e Hibernate, consulta questo articolo .

Uno a uno

La relazione della tabella uno a uno ha il seguente aspetto:

Uno a uno

In un sistema di database relazionale, una relazione di tabella uno a uno collega due tabelle basate su una Primary Keycolonna nel figlio che fa anche Foreign Keyriferimento alla Primary Keyriga della tabella padre.

Pertanto, possiamo dire che la tabella figlio condivide la Primary Keycon la tabella padre.

Nel diagramma della tabella sopra, la idcolonna nella post_detailstabella ha anche una Foreign Keyrelazione con la colonna della posttabella id Primary Key:

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

Utilizzo dell'APP @OneToOnecon @MapsIdannotazioni

Il modo migliore per mappare una @OneToOnerelazione è usare @MapsId. In questo modo, non è nemmeno necessaria un'associazione bidirezionale poiché è sempre possibile recuperare l' PostDetailsentità utilizzando l' Postidentificatore di entità.

La mappatura è simile alla seguente:

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

@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

} [/codice]

In questo modo, la idproprietà funge sia da chiave primaria che da chiave esterna. Noterai che la @Idcolonna non utilizza più @GeneratedValueun'annotazione poiché l'identificatore è popolato con l'identificatore postdell'associazione.

Per ulteriori dettagli sul modo migliore per mappare la @OneToOnerelazione con JPA e Hibernate, consulta questo articolo .

Molti-a-molti

La relazione della tabella molti-a si presenta come segue:

Molti-a-molti

In un sistema di database relazionale, una relazione di tabella molti-a-molti collega due tabelle padre tramite una tabella figlio che contiene due Foreign Keycolonne che fanno riferimento alle Primary Keycolonne delle due tabelle padre.

Nel diagramma della tabella sopra, la post_idcolonna nella post_tagtabella ha anche una Foreign Keyrelazione con la colonna postID tabella Primary Key:

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

Inoltre, la tag_idcolonna nella post_tagtabella ha una Foreign Keyrelazione con la colonna tagID tabella Primary Key:

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

Utilizzo del @ManyToManymapping JPA

Ecco come è possibile mappare la many-to-manyrelazione della tabella con JPA e 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' tagsassociazione Postnell'entità definisce solo i tipi PERSISTe in MERGEcascata. Come spiegato in questo articolo , la REMOVE transizione dello stato dell'entità non ha alcun senso per un'associazione @ManyToManyJPA poiché potrebbe innescare una cancellazione della catena che alla fine cancellerebbe entrambi i lati dell'associazione.
  2. Come spiegato in questo articolo , i metodi di utilità Aggiungi / Rimuovi sono obbligatori se si utilizzano associazioni bidirezionali in modo da poter assicurarsi che entrambi i lati dell'associazione siano sincronizzati.
  3. L' Postentità utilizza l'identificatore di entità per l'uguaglianza poiché non dispone di alcuna chiave aziendale univoca. Come spiegato in questo articolo , è possibile utilizzare l'identificatore di entità per l'uguaglianza purché si assicuri che rimanga coerente in tutte le transizioni di stato dell'entità .
  4. L' Tagentità ha una chiave aziendale univoca contrassegnata con l' @NaturalIdannotazione specifica di Hibernate . In tal caso, la chiave aziendale unica è il miglior candidato per i controlli di uguaglianza .
  5. L' mappedByattributo postsdell'associazione Tagnell'entità indica che, in questa relazione bidirezionale, l' Postentità possiede l'associazione. Ciò è necessario poiché solo una parte può possedere una relazione e le modifiche vengono propagate al database solo da questa parte specifica.
  6. È Setpreferibile, poiché l'uso di Listcon @ManyToManyè meno efficiente.

Per ulteriori dettagli sul modo migliore per mappare la @ManyToManyrelazione con JPA e Hibernate, consulta questo articolo .


1

questo richiederebbe probabilmente una nave di relazione molti-a-molti come segue



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
}

potresti dover definire un joinTable + JoinColumn ma funzionerà anche senza ...


1

Spiegherei in questo modo:

OneToOne: relazione OneToOne

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany: relazione ManyToOne

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany: relazione ManyToMany

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

0

Prima di tutto, leggi tutta la stampa fine. Si noti che la mappatura relazionale di NHibernate (quindi, presumo, anche Hibernate) ha una corrispondenza divertente con DB e mappatura di oggetti. Ad esempio, le relazioni uno a uno sono spesso implementate come una relazione molti a uno.

In secondo luogo, prima di poterti dire come scrivere la tua mappa O / R, dobbiamo vedere anche il tuo DB. In particolare, una singola abilità può essere posseduta da più persone? In tal caso, hai una relazione molti-a-molti; altrimenti, è molti-a-uno.

In terzo luogo, preferisco non implementare direttamente le relazioni molti-a-molti, ma piuttosto modellare la "tabella di join" nel modello di dominio, ovvero trattarla come un'entità, in questo modo:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Allora vedi quello che hai? Hai due relazioni uno-a-molti. (In questo caso, Person potrebbe avere una collezione di PersonSkills, ma non avrebbe una collezione di Skills.) Tuttavia, alcuni preferiranno usare una relazione molti-a-molti (tra Person e Skill); questo è controverso.

In quarto luogo, se hai relazioni bidirezionali (ad esempio, Person non ha solo una collezione di Abilità, ma anche Abilità ha una collezione di Persone), NHibernate non impone la bidirezionalità nel tuo BL per te; comprende solo la bidirezionalità delle relazioni ai fini della persistenza.

In quinto luogo, molti-a-uno è molto più facile da usare correttamente in NHibernate (e presumo Hibernate) rispetto a uno-a-molti (mappatura della raccolta).

In bocca al lupo!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.