¿Diferencia entre uno a muchos, muchos a uno y muchos a muchos?


144

Ok, esta es probablemente una pregunta trivial, pero tengo problemas para visualizar y comprender las diferencias y cuándo usar cada una. Tampoco estoy un poco claro sobre cómo los conceptos como los mapeos unidireccionales y bidireccionales afectan las relaciones uno a muchos / muchos a muchos. Estoy usando Hibernate en este momento, por lo que cualquier explicación relacionada con ORM será útil.

Como ejemplo, digamos que tengo la siguiente configuración:

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
}

Entonces, en este caso, ¿qué tipo de mapeo tendría? Las respuestas a este ejemplo específico son definitivamente apreciadas, pero también me gustaría una visión general de cuándo usar uno a muchos y muchos a muchos y cuándo usar una tabla de unión frente a una columna de unión y unidireccional versus bidireccional.


11
Parece que todos solo responden Uno a muchos versus Muchos a muchos. Mire mi respuesta para Uno a muchos versus Muchos a uno.
Alexander Suraphel el

Respuestas:


165

Uno a muchos : una persona tiene muchas habilidades, una habilidad no se reutiliza entre personas

  • Unidireccional : una persona puede hacer referencia directa a las habilidades a través de su conjunto
  • Bidireccional : cada habilidad "secundaria" tiene un puntero único que respalda a la Persona (que no se muestra en su código)

Muchos a muchos : una persona tiene muchas habilidades, una habilidad se reutiliza entre personas

  • Unidireccional : una persona puede hacer referencia directa a las habilidades a través de su conjunto
  • Bidireccional : una habilidad tiene un conjunto de personas relacionadas con ella.

En una relación Uno a Muchos, un objeto es el "padre" y el otro es el "hijo". El padre controla la existencia del niño. En un Many-To-Many, la existencia de cualquiera de los dos tipos depende de algo externo a ambos (en el contexto de aplicación más amplio).

Su tema (dominio) debe dictar si la relación es de uno a muchos o de muchos a muchos; sin embargo, creo que hacer que la relación sea unidireccional o bidireccional es una decisión de ingeniería que intercambia memoria, procesamiento y rendimiento. etc.

¡Lo que puede ser confuso es que una relación bidireccional de muchos a muchos no necesita ser simétrica! Es decir, un grupo de personas podría señalar una habilidad, pero la habilidad no necesita relacionarse solo con esas personas. Típicamente lo haría, pero tal simetría no es un requisito. Tomemos el amor, por ejemplo, es bidireccional ("I-Love", "Loves-Me"), pero a menudo asimétrico ("La amo, pero ella no me ama").

Todos estos están bien respaldados por Hibernate y JPA. Solo recuerde que Hibernate o cualquier otro ORM no se preocupa por mantener la simetría al administrar relaciones bidireccionales de muchos a muchos ... eso depende de la aplicación.


Para aclarar, cualquier relación puede ser unidireccional o bidireccional en su BL o en su mapeo O / R (¡independientemente el uno del otro, incluso!).
jyoungdev

44
El ejemplo de "AMOR" simplemente lo aclaró. ManyToMany es mi tipo de mapeo.
Abdullah Khan

1
Súper. Esto lo explica muy bien (y en el contexto del ejemplo de OP)
Anupam

1
No responde la pregunta correctamente. Echas de menos los muchos a una parte. aunque uno a muchos y muchos a uno es una cuestión de percepción, esta respuesta no menciona eso.
Manzur Alahi

248

Se parece a todo el mundo está respondiendo One-to-manyfrente a Many-to-many:

La diferencia entre One-to-many, Many-to-oney Many-to-Manyes:

One-to-manyvs Many-to-onees una cuestión de perspectiva . Unidirectionalvs Bidirectionalno afectará la asignación, pero hará una diferencia en cómo puede acceder a sus datos.

  • En Many-to-oneel manylado mantendrá la referencia del onelado. Un buen ejemplo es "Un estado tiene ciudades". En este caso Statees el lado uno y Cityes el lado múltiple. Habrá una columna state_iden la tabla cities.

En unidireccional , la Personclase tendrá List<Skill> skillspero Skillno tendrá Person person. En bidireccional , se agregan ambas propiedades y le permite acceder a Personuna habilidad determinada (es decir skill.person).

  • Por One-to-Manyun lado estará nuestro punto de referencia. Por ejemplo, "Un usuario tiene una dirección". En este caso, podríamos tener tres columnas address_1_id, address_2_idy / address_3_ido una tabla de búsqueda con restricción única en user_idy address_id.

En unidireccional , un Usertendrá Address address. Bidireccional tendrá un adicional List<User> usersen la Addressclase.

  • En los Many-to-Manymiembros de cada parte se puede hacer referencia al número arbitrario de miembros de la otra parte. Para lograr esto, se utiliza una tabla de consulta . Ejemplo de esto es la relación entre médicos y pacientes. Un médico puede tener muchos pacientes y viceversa.

27
esta debería ser la respuesta aceptada, la mayoría de las otras respuestas pierden la pregunta.
arg20

El primer ejemplo es incorrecto. Si tiene A Person y Person tiene @OneToMany with Skills, esa tabla preson_skills tendría una restricción única en skill_id. Entonces, una habilidad se asignaría solo a una persona. Y no se puede extraer s.persons, ya que solo hays.person
Иван Николайчук

En realidad, la One-to-manyrelación como la describe es una Many-to-manyrelación porque persontiene una referencia a muchos skillspero skillno mantiene una referencia a una persona en particular y muchos personspueden tener una referencia igual skill. Y su Many-to-onerelación es en realidad One-to-manyporque cada habilidad tiene una referencia solo a una, personya que un niño solo tiene una madre.
mixel

@mixel tu comentario terminó haciéndome reescribir la mayor parte de mi respuesta. ¡Por favor revísalo de nuevo! Gracias
Alexander Suraphel

Usted distingue entre uno a muchos y muchos a uno por el significado del lado "uno". En ambos ejemplos, "Usuario" es la entidad más importante. Entonces, cuando está en el "Uno" (usuarios y habilidades, skillstiene person_id), entonces lo llamas Uno a muchos, pero cuando es uno, el lado "Muchos" (usuarios y direcciones, userstiene address_id), entonces lo llamas Muchos a uno. Pero estructuralmente ambos casos son idénticos y se llaman Uno a muchos.
mixel

38

1) Los círculos son Entidades / POJO / Frijoles

2) deg es una abreviatura de grado como en gráficos (número de aristas)

PK = Clave primaria, FK = Clave externa

Tenga en cuenta la contradicción entre el grado y el nombre del lado. Muchos corresponde a grado = 1 mientras que Uno corresponde a grado> 1.

Ilustración de uno a muchos muchos a uno


1
Realmente me encanta cómo vincula el gráfico de objetos a las tablas en ambas direcciones.
Dmitry Minkovsky

3
Mira nerds, así es como se ve la
ESCRITURA A

Veo lo que hiciste aquí.
Nick Gallimore

8

Eche un vistazo a este artículo: Asignación de relaciones de objetos

Hay dos categorías de relaciones de objeto con las que debe preocuparse al mapear. La primera categoría se basa en la multiplicidad e incluye tres tipos:

*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 segunda categoría se basa en la direccionalidad y contiene dos tipos, relaciones unidireccionales y relaciones bidireccionales.

*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

Esta es una pregunta muy común, por lo que esta respuesta se basa en este artículo que escribí en mi blog.

Uno a muchos

La relación de la tabla uno a muchos se ve de la siguiente manera:

Uno a muchos

En un sistema de base de datos relacional, una relación de tabla de uno a muchos vincula dos tablas basadas en una Foreign Keycolumna en el elemento secundario que hace referencia a la Primary Keyfila de la tabla principal.

En el diagrama de la tabla anterior, la post_idcolumna en la post_commenttabla tiene una Foreign Keyrelación con la columna de postid de la tabla Primary Key:

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

Anotación @ManyToOne

La mejor manera de mapear la relación de la tabla uno a muchos es usar la @ManyToOneanotación.

En nuestro caso, la entidad secundaria PostCommentasigna la post_idcolumna Clave externa utilizando la @ManyToOneanotación:

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

}

Usando la @OneToManyanotación JPA

El hecho de que tenga la opción de usar la @OneToManyanotación, no significa que esta debería ser la opción predeterminada para cada relación de base de datos de uno a muchos . El problema con las colecciones es que solo podemos usarlas cuando el número de registros secundarios es bastante limitado.

La mejor manera de mapear una @OneToManyasociación es confiar en el @ManyToOnelado para propagar todos los cambios de estado de la entidad:

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

La entidad principal Post, presenta dos métodos de utilidad (por ejemplo, addCommenty removeComment) que se utilizan para sincronizar ambos lados de la asociación bidireccional. Siempre debe proporcionar estos métodos siempre que trabaje con una asociación bidireccional ya que, de lo contrario, corre el riesgo de tener problemas de propagación de estado muy sutiles .

La @OneToManyasociación unidireccional debe evitarse ya que es menos eficiente que usar @ManyToOneo la @OneToManyasociación bidireccional .

Para obtener más detalles sobre la mejor manera de mapear la @OneToManyrelación con JPA e Hibernate, consulte este artículo .

Doce y cincuenta y nueve de la noche

La relación de la tabla uno a uno se ve de la siguiente manera:

Doce y cincuenta y nueve de la noche

En un sistema de base de datos relacional, una relación de tabla uno a uno vincula dos tablas basadas en una Primary Keycolumna en el elemento secundario que también hace Foreign Keyreferencia a la Primary Keyfila de la tabla principal.

Por lo tanto, podemos decir que la tabla secundaria comparte Primary Keycon la tabla primaria.

En el diagrama de la tabla anterior, la idcolumna de la post_detailstabla también tiene una Foreign Keyrelación con la columna de la posttabla id Primary Key:

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

Usando el JPA @OneToOnecon @MapsIdanotaciones

La mejor manera de mapear una @OneToOnerelación es usarla @MapsId. De esta manera, ni siquiera necesita una asociación bidireccional, ya que siempre puede buscar la PostDetailsentidad utilizando el Postidentificador de entidad.

El mapeo se ve así:

[code language = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") clase pública 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

} [/ código]

De esta manera, la idpropiedad sirve como clave principal y clave externa. Notará que la @Idcolumna ya no usa una @GeneratedValueanotación ya que el identificador se llena con el identificador de la postasociación.

Para obtener más detalles sobre la mejor manera de mapear la @OneToOnerelación con JPA e Hibernate, consulte este artículo .

Muchos a muchos

La relación de tabla de muchos a muchos tiene el siguiente aspecto:

Muchos a muchos

En un sistema de base de datos relacional, una relación de tabla de muchos a muchos vincula dos tablas principales a través de una tabla secundaria que contiene dos Foreign Keycolumnas que hacen referencia a las Primary Keycolumnas de las dos tablas principales.

En el diagrama de la tabla anterior, la post_idcolumna de la post_tagtabla también tiene una Foreign Keyrelación con la columna de postID de la tabla Primary Key:

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

Y, la tag_idcolumna en la post_tagtabla tiene una Foreign Keyrelación con la columna de tagid de la tabla Primary Key:

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

Usando el @ManyToManymapeo JPA

Así es como puede mapear la many-to-manyrelación de la tabla 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. La tagsasociación en la Postentidad solo define los tipos PERSISTy MERGEcascada. Como se explica en este artículo , la REMOVE transición del estado de la entidad no tiene ningún sentido para una @ManyToManyasociación JPA, ya que podría desencadenar una eliminación de la cadena que finalmente eliminaría ambos lados de la asociación.
  2. Como se explica en este artículo , los métodos de utilidad de agregar / quitar son obligatorios si usa asociaciones bidireccionales para asegurarse de que ambos lados de la asociación estén sincronizados.
  3. La Postentidad utiliza el identificador de entidad para la igualdad, ya que carece de una clave comercial única. Como se explica en este artículo , puede usar el identificador de entidad para la igualdad siempre que se asegure de que sea coherente en todas las transiciones de estado de entidad .
  4. La Tagentidad tiene una clave comercial única que está marcada con la @NaturalIdanotación específica de Hibernate . Cuando ese es el caso, la clave comercial única es el mejor candidato para los controles de igualdad .
  5. El mappedByatributo de la postsasociación en la Tagentidad marca que, en esta relación bidireccional, la Postentidad posee la asociación. Esto es necesario ya que solo un lado puede ser dueño de una relación, y los cambios solo se propagan a la base de datos desde este lado en particular.
  6. El Setes preferible, ya que usar un Listcon @ManyToManyes menos eficiente.

Para obtener más detalles sobre la mejor manera de mapear la @ManyToManyrelación con JPA e Hibernate, consulte este artículo .


1

esto probablemente requeriría una relación de muchos a muchos de la siguiente manera



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
}

Es posible que deba definir un joinTable + JoinColumn, pero es posible que funcione también sin ...


1

Yo explicaría de esa manera:

OneToOne - OneToOne relación

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - ManyToOne relación

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - Relación ManyToMany

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

0

En primer lugar, lea toda la letra pequeña. Tenga en cuenta que el mapeo relacional de NHibernate (por lo tanto, supongo, Hibernate también) tiene una correspondencia divertida con DB y el mapeo de gráficos de objetos. Por ejemplo, las relaciones uno a uno a menudo se implementan como una relación muchos a uno.

En segundo lugar, antes de que podamos decirle cómo debe escribir su mapa O / R, también tenemos que ver su base de datos. En particular, ¿puede una sola habilidad ser poseída por varias personas? Si es así, tienes una relación de muchos a muchos; de lo contrario, es muchos a uno.

En tercer lugar, prefiero no implementar relaciones de muchos a muchos directamente, sino modelar la "tabla de unión" en su modelo de dominio, es decir, tratarla como una entidad, como esta:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

¿Entonces ves lo que tienes? Tienes dos relaciones uno a muchos. (En este caso, la Persona puede tener una colección de Habilidades Personales, pero no tendría una colección de Habilidades). Sin embargo, algunos preferirán usar una relación de muchos a muchos (entre Persona y Habilidad); Esto es controvertido.

Cuarto, si tiene relaciones bidireccionales (por ejemplo, la Persona no solo tiene una colección de Habilidades, sino que también tiene una colección de Personas), NHibernate no aplica la bidireccionalidad en su BL para usted; solo comprende la bidireccionalidad de las relaciones con fines de persistencia.

Quinto, muchos a uno es mucho más fácil de usar correctamente en NHibernate (y supongo que Hibernate) que uno a muchos (mapeo de colección).

¡Buena suerte!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.