Diferença entre um para muitos, muitos para um e muitos para muitos?


144

Ok, então essa é provavelmente uma pergunta trivial, mas estou tendo problemas para visualizar e entender as diferenças e quando usar cada uma. Também sou um pouco incerto sobre como conceitos como mapeamentos unidirecionais e bidirecionais afetam os relacionamentos um para muitos / muitos para muitos. Estou usando o Hibernate agora, então qualquer explicação relacionada ao ORM será útil.

Como exemplo, digamos que tenho a seguinte configuração:

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
}

Então, nesse caso, que tipo de mapeamento eu teria? As respostas a este exemplo específico são definitivamente apreciadas, mas eu também gostaria de ter uma visão geral de quando usar um para muitos e muitos para muitos e quando usar uma tabela de junção versus uma coluna de junção e unidirecional versus bidirecional.


11
Parece que todo mundo está respondendo apenas um para muitos vs muitos para muitos. Veja a minha resposta para Um para muitos vs Muitos para um.
Alexander Suraphel

Respostas:


165

Um para muitos : uma pessoa tem muitas habilidades, uma habilidade não é reutilizada entre as pessoas

  • Unidirecional : Uma Pessoa pode referenciar diretamente Habilidades através de seu Conjunto
  • Bidirecional : cada habilidade "filho" tem um único ponteiro de volta para a pessoa (o que não é mostrado no seu código)

Muitos para muitos : uma pessoa tem muitas habilidades, uma habilidade é reutilizada entre as pessoas

  • Unidirecional : Uma Pessoa pode referenciar diretamente Habilidades através de seu Conjunto
  • Bidirecional : Uma Habilidade possui um Conjunto de Pessoas que se relacionam a ela.

Em um relacionamento um para muitos, um objeto é o "pai" e o outro é o "filho". O pai controla a existência do filho. Em muitos para muitos, a existência de qualquer tipo depende de algo fora dos dois (no contexto maior do aplicativo).

O assunto (domínio) deve determinar se o relacionamento é ou não um para muitos ou muitos para muitos - no entanto, acho que tornar o relacionamento unidirecional ou bidirecional é uma decisão de engenharia que troca memória, processamento, desempenho etc.

O que pode ser confuso é que um relacionamento bidirecional muitos-para-muitos não precisa ser simétrico! Ou seja, várias pessoas podem apontar para uma habilidade, mas a habilidade não precisa se relacionar apenas com essas pessoas. Normalmente, mas essa simetria não é um requisito. Pegue o amor, por exemplo - é bidirecional ("Eu amo", "Me ama"), mas geralmente assimétrico ("Eu a amo, mas ela não me ama")!

Todos estes são bem suportados pelo Hibernate e JPA. Lembre-se de que o Hibernate ou qualquer outro ORM não se preocupa em manter a simetria ao gerenciar relacionamentos bidirecionais de muitos para muitos ... tudo depende do aplicativo.


Para esclarecer, qualquer relacionamento pode ser uni ou bidirecional no seu BL ou no seu mapeamento de O / R (independentemente um do outro, até!).
jyoungdev

4
O exemplo "LOVE" apenas o esclareceu. ManyToMany é o meu tipo de mapeamento.
Abdullah Khan

1
Super. Isso explica tão bem (e no contexto do exemplo de OP)
Anupam

1
Não responde a pergunta corretamente. você sente falta de muitas partes. embora um para muitos e muitos para um seja uma questão de percepção, essa resposta não menciona isso.
Manzur Alahi

248

Looks como todo mundo está respondendo One-to-manyvs. Many-to-many:

A diferença entre One-to-many, Many-to-onee Many-to-Manyé:

One-to-manyvs Many-to-oneé uma questão de perspectiva . Unidirectionalvs Bidirectionalnão afetará o mapeamento, mas fará diferença em como você pode acessar seus dados.

  • Em Many-to-onedo manylado manterá referência do onelado. Um bom exemplo é "Um Estado tem Cidades". Nesse caso, Stateé o lado único e Cityo lado múltiplo. Haverá uma coluna state_idna tabela cities.

Em unidirecional , a Personclasse terá, List<Skill> skillsmas Skillnão terá Person person. Em bidirecional , as duas propriedades são adicionadas e permite acessar uma Persondeterminada habilidade (ou seja skill.person).

  • De One-to-Manyum lado, será o nosso ponto de referência. Por exemplo, "Um usuário tem um endereço". Neste caso, pode ter três colunas address_1_id, address_2_ide address_3_idou um olhar para cima da tabela com restrição exclusiva em user_ide address_id.

Em unidirecional , a Userterá Address address. Bidirecional terá um adicional List<User> usersna Addressclasse.

  • Nos Many-to-Manymembros de cada parte, pode-se manter referência ao número arbitrário de membros da outra parte. Para isso, é utilizada uma tabela de consulta . Exemplo disso é a relação entre médicos e pacientes. Um médico pode ter muitos pacientes e vice-versa.

27
essa deve ser a resposta aceita, a maioria das outras respostas perde a pergunta.
arg20

O primeiro exemplo está incorreto. Se você tem uma pessoa e uma pessoa possui @OneToMany with Skills, essa tabela preson_skills teria uma restrição exclusiva em skill_id. Portanto, uma habilidade seria mapeada apenas para uma pessoa. E você não pode extrair s.persons, como só existes.person
Иван Николайчук

Na verdade, o One-to-manyrelacionamento que você descreve é ​​um Many-to-manyrelacionamento, porque persontem uma referência a muitos, skillsmas skillnão mantém referência a uma pessoa em particular e muitos personspodem ter a mesma referência skill. E seu Many-to-onerelacionamento é realmente One-to-manyporque cada habilidade tem uma referência apenas a uma personquando a criança tem apenas uma mãe.
mixel

@mixel, seu comentário acabou me fazendo reescrever a maior parte da minha resposta. Por favor, verifique novamente! Obrigado
Alexander Suraphel

Você distingue entre Um para muitos e Muitos para um pelo significado do lado "Um". Nos dois exemplos, "Usuário" é a entidade mais significativa. Então, quando está no "Um" (usuários e habilidades, skillstem person_id), você o chama de Um para muitos, mas quando está no lado "Muitos" (usuários e endereços, userstem address_id), então você o chama de Muitos para um. Mas, estruturalmente, os dois casos são idênticos e chamados de um para muitos.
mixel

38

1) Os círculos são Entidades / POJOs / Feijões

2) deg é uma abreviação de degree como nos gráficos (número de arestas)

PK = Chave primária, FK = Chave estrangeira

Observe a contradição entre o grau e o nome do lado. Muitos correspondem ao grau = 1, enquanto Um corresponde ao grau> 1.

Ilustração de um para muitos muitos para um


1
Realmente ame como ele vincula o gráfico do objeto às tabelas nas duas direções.
Dmitry Minkovsky

3
Veja os nerds, é assim que HANDWRITING do PROGRAMMER se parece: D
Mehraj Malik

Eu vejo o que você fez aqui.
precisa

8

Dê uma olhada neste artigo: Mapeando Relacionamentos de Objetos

Existem duas categorias de relacionamentos de objetos com as quais você precisa se preocupar ao mapear. A primeira categoria é baseada na multiplicidade e inclui três 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. 

A segunda categoria é baseada na direcionalidade e contém dois tipos, relações unidirecionais e relações bidirecionais.

*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 é uma pergunta muito comum, portanto, esta resposta é baseada neste artigo que escrevi no meu blog.

Um para muitos

O relacionamento de uma para muitas tabelas é o seguinte:

Um para muitos

Em um sistema de banco de dados relacional, um relacionamento de tabela um para muitos vincula duas tabelas com base em uma Foreign Keycoluna no filho que faz referência à Primary Keylinha da tabela pai.

No diagrama da tabela acima, a post_idcoluna da post_commenttabela possui um Foreign Keyrelacionamento com a coluna do postID da tabela Primary Key:

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

Anotação @ManyToOne

A melhor maneira de mapear o relacionamento de uma para muitas tabelas é usar a @ManyToOneanotação.

No nosso caso, a entidade filha PostCommentmapeia a post_idcoluna Chave estrangeira usando a @ManyToOneanotação:

@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 a @OneToManyanotação JPA

Só porque você tem a opção de usar a @OneToManyanotação, isso não significa que essa deve ser a opção padrão para todo relacionamento de banco de dados um para muitos . O problema com as coleções é que só podemos usá-las quando o número de registros filhos é bastante limitado.

A melhor maneira de mapear uma @OneToManyassociação é confiar no @ManyToOnelado para propagar todas as alterações de estado da entidade:

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

A entidade pai Post, apresenta dois métodos utilitários (por exemplo, addCommente removeComment) que são usados ​​para sincronizar os dois lados da associação bidirecional. Você deve sempre fornecer esses métodos sempre que estiver trabalhando com uma associação bidirecional, pois, caso contrário, corre o risco de problemas de propagação de estado muito sutis .

A @OneToManyassociação unidirecional deve ser evitada, pois é menos eficiente do que o uso @ManyToOneou a @OneToManyassociação bidirecional .

Para obter mais detalhes sobre a melhor maneira de mapear o @OneToManyrelacionamento com JPA e Hibernate, consulte este artigo .

Um a um

O relacionamento da tabela um para um é o seguinte:

Um a um

Em um sistema de banco de dados relacional, um relacionamento de tabela um para um vincula duas tabelas com base em uma Primary Keycoluna no filho, que também é uma Foreign Keyreferência à Primary Keylinha da tabela pai.

Portanto, podemos dizer que a tabela filho compartilha Primary Keycom a tabela pai.

No diagrama da tabela acima, a idcoluna na post_detailstabela também possui um Foreign Keyrelacionamento com a coluna da posttabela id Primary Key:

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

Usando o JPA @OneToOnecom@MapsId anotações

A melhor maneira de mapear um @OneToOnerelacionamento é usar @MapsId. Dessa forma, você nem precisa de uma associação bidirecional, pois sempre pode buscar a PostDetailsentidade usando o métodoPost identificador entidade.

O mapeamento fica assim:

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

Dessa forma, a idpropriedade serve como Chave Primária e Chave Externa. Você notará que a @Idcoluna não usa mais uma @GeneratedValueanotação, pois o identificador é preenchido com o identificador da postassociação.

Para obter mais detalhes sobre a melhor maneira de mapear o @OneToOnerelacionamento com JPA e Hibernate, consulte este artigo .

Muitos para muitos

O relacionamento de tabela muitos para muitos é o seguinte:

Muitos para muitos

Em um sistema de banco de dados relacional, um relacionamento de tabela muitos para muitos vincula duas tabelas pai por meio de uma tabela filha que contém duas Foreign Keycolunas referenciando as Primary Keycolunas das duas tabelas pai.

No diagrama da tabela acima, a post_idcoluna da post_tagtabela também possui um Foreign Keyrelacionamento com a coluna do postID da tabela Primary Key:

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

E, a tag_idcoluna na post_tagtabela possui um Foreign Keyrelacionamento com a coluna de tagidentificação da tabela Primary Key:

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

Usando o @ManyToManymapeamento JPA

É assim que você pode mapear o many-to-manyrelacionamento da tabela com 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. A tagsassociação na Postentidade define apenas os tipos PERSISTe MERGEcascata. Conforme explicado neste artigo , a REMOVE transição de estado da entidade não faz sentido para uma @ManyToManyassociação JPA, pois pode desencadear uma exclusão de cadeia que acabaria com os dois lados da associação.
  2. Conforme explicado neste artigo , os métodos do utilitário adicionar / remover são obrigatórios se você usar associações bidirecionais para garantir que os dois lados da associação estejam sincronizados.
  3. A Postentidade usa o identificador de entidade para igualdade, uma vez que não possui nenhuma chave comercial exclusiva. Conforme explicado neste artigo , você pode usar o identificador de entidade para igualdade, desde que ele se mantenha consistente em todas as transições de estado da entidade .
  4. A Tagentidade possui uma chave comercial exclusiva, marcada com a @NaturalIdanotação específica do Hibernate . Nesse caso, a chave comercial exclusiva é o melhor candidato para verificações de igualdade .
  5. O mappedByatributo da postsassociação na Tagentidade marca que, nesse relacionamento bidirecional, oPost entidade possui a associação. Isso é necessário, pois apenas um lado pode possuir um relacionamento e as alterações são propagadas apenas para o banco de dados desse lado específico.
  6. O Setdeve ser preferido, pois usar um Listcom @ManyToManyé menos eficiente.

Para obter mais detalhes sobre a melhor maneira de mapear o @ManyToManyrelacionamento com JPA e Hibernate, consulte este artigo .


1

isso provavelmente exigiria uma relação de muitos para muitos da seguinte maneira



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
}

pode ser necessário definir um joinTable + JoinColumn, mas também será possível trabalhar sem ...


1

Eu explicaria assim:

OneToOne - relacionamento OneToOne

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - relacionamento ManyToOne

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - relacionamento ManyToMany

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

0

Antes de tudo, leia todas as letras pequenas. Observe que o mapeamento relacional do NHibernate (portanto, eu também assumo o Hibernate) tem uma correspondência engraçada com o mapeamento de banco de dados e gráfico de objetos. Por exemplo, os relacionamentos um para um são frequentemente implementados como um relacionamento muitos para um.

Segundo, antes que possamos dizer como você deve escrever seu mapa de O / R, também precisamos ver seu banco de dados. Em particular, uma única Habilidade pode ser possuída por várias pessoas? Nesse caso, você tem um relacionamento muitos-para-muitos; caso contrário, é muitos-para-um.

Terceiro, prefiro não implementar relacionamentos muitos-para-muitos diretamente, mas sim modelar a "tabela de junção" em seu modelo de domínio - ou seja, tratá-la como uma entidade, assim:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Então você vê o que tem? Você tem dois relacionamentos um para muitos. (Nesse caso, a pessoa pode ter uma coleção de habilidades pessoais, mas não teria uma coleção de habilidades.) No entanto, alguns preferem usar o relacionamento muitos-para-muitos (entre pessoa e habilidade); isso é controverso.

Quarto, se você tiver relacionamentos bidirecionais (por exemplo, não apenas a Pessoa possui uma coleção de Habilidades, mas também a Skill possui uma coleção de Pessoas), o NHibernate não impõe a bidirecionalidade no seu BL para você; entende apenas a bidirecionalidade dos relacionamentos para fins de persistência.

Em quinto lugar, é muito mais fácil usar corretamente um no outro para um no NHibernate (e eu assumo o Hibernate) do que um para muitos (mapeamento de coleção).

Boa sorte!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.