Qual é a diferença entre as associações JPA unidirecional e bidirecional e Hibernate?


135

Qual é a diferença entre associações unidirecionais e bidirecionais?

Como a tabela gerada no banco de dados é a mesma, a única diferença que encontrei é que cada lado das associações bidirecionais fará referência ao outro, e o unidirecional não.

Esta é uma associação unidirecional

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}

public class Group {
    private int     id;
    private String  name;
}

A associação bidirecional

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}
public class Group {
    private int         id;
    private String      name;
    @OneToMany(mappedBy="group")
    private List<User>  users;
}

A diferença é se o grupo possui uma referência do usuário.

Então, eu me pergunto se essa é a única diferença? o que é recomendado?


7
O grupo agora saberá quais usuários ele contém. Eu não acho que isso seja de forma alguma uma pequena diferença.
— Satadru Biswas 19/03/11

5
Os relacionamentos bidirecionais se tornaram um caos para mim quando se trata de atualização. :)
— diyoda_

Respostas:


152

A principal diferença é que o relacionamento bidirecional fornece acesso de navegação nas duas direções, para que você possa acessar o outro lado sem consultas explícitas. Também permite aplicar opções em cascata nas duas direções.

Observe que o acesso à navegação nem sempre é bom, especialmente para os relacionamentos "um para muitos" e "muitos para muitos". Imagine um Groupque contém milhares de Users:

  • Como você os acessaria? Com tantos Users, você geralmente precisa aplicar alguma filtragem e / ou paginação, para executar uma consulta de qualquer maneira (a menos que você use a filtragem de coleção , que parece um hack para mim). Alguns desenvolvedores tendem a aplicar filtragem na memória nesses casos, o que obviamente não é bom para o desempenho. Observe que esse relacionamento pode incentivar esse tipo de desenvolvedor a usá-lo sem considerar as implicações de desempenho.

  • Como você adicionaria novos Users ao Group? Felizmente, o Hibernate olha para o lado proprietário do relacionamento ao persistir, para que você possa configurá-lo apenas User.group. No entanto, se você deseja manter os objetos na memória consistentes, também precisa adicionar Usera Group.users. Mas isso faria o Hibernate buscar todos os elementos do Group.usersbanco de dados!

Portanto, não posso concordar com a recomendação das Melhores Práticas . Você precisa projetar relacionamentos bidirecionais com cuidado, considerando os casos de uso (você precisa de acesso de navegação nas duas direções?) E possíveis implicações de desempenho.

Veja também:


Olá, obrigado, parece que você é um especialista em hibernação, pois respondeu à minha pergunta: stackoverflow.com/questions/5350770/… . E agora eu não tenho certeza o relacionamento exploração, já que não posso escrever mais no comentário, então eu postar aqui dpaste.de/J85m .Please ter uma verificação se possível :).
— hguser

@ hguser: Se você finalmente decidir tornar seu relacionamento bidirecional, acho que seria melhor ligar setGroup()de addUser()para manter os dois lados consistentes.
— axtavt

e se eu não chamar o setGroup () dentro do group.addUser ()?
— Hguser

@ hguser: O relacionamento não seria persistente, veja o ponto 2 da resposta. Você pode chamar setGroup()sem addUser(), mas isso resultaria em um estado inconsistente de objetos na memória.
— axtavt

Descobri que não consigo um mapeamento correto, você pode poupar algum tempo para verificar meu projeto no github? é um pequeno projeto.
— Hguser 26/03/11

31

Existem duas diferenças principais.

Acessando os lados da associação

O primeiro está relacionado a como você acessará o relacionamento. Para uma associação unidirecional, você pode navegar na associação somente de uma extremidade.

Portanto, para uma @ManyToOneassociação unidirecional , isso significa que você só pode acessar o relacionamento do lado filho em que a chave estrangeira reside.

Se você tiver uma @OneToManyassociação unidirecional , isso significa que você só pode acessar o relacionamento do lado pai onde reside a chave estrangeira.

Para a @OneToManyassociação bidirecional , você pode navegar na associação nos dois sentidos, a partir do pai ou do filho.

Você também precisa usar métodos de utilidade adicionar / remover para associações bidirecionais para garantir que ambos os lados estejam sincronizados corretamente .

atuação

O segundo aspecto está relacionado ao desempenho.

  1. Para @OneToMany, associações unidirecionais não executar, bem como aqueles bidirecionais .
  2. Pois @OneToOne, uma associação bidirecional fará com que o pai seja buscado ansiosamente se o Hibernate não puder dizer se o Proxy deve ser atribuído ou um valor nulo .
  3. Pois @ManyToMany, o tipo de coleção faz muita diferença, pois Setsapresenta um desempenho melhor queLists .

11

Em termos de codificação, um relacionamento bidirecional é mais complexo de implementar porque o aplicativo é responsável por manter os dois lados sincronizados de acordo com a especificação 5 da JPA (na página 42). Infelizmente, o exemplo dado na especificação não fornece mais detalhes, portanto não fornece uma idéia do nível de complexidade.

Quando não está usando um cache de segundo nível, geralmente não é um problema não ter os métodos de relacionamento implementados corretamente porque as instâncias são descartadas no final da transação.

Ao usar o cache de segundo nível, se algo for corrompido devido a métodos de manipulação de relacionamento implementados incorretamente, isso significa que outras transações também verão os elementos corrompidos (o cache de segundo nível é global).

Um relacionamento bidirecional corretamente implementado pode tornar as consultas e o código mais simples, mas não deve ser usado se realmente não fizer sentido em termos de lógica de negócios.


9

Não tenho 100% de certeza de que essa é a única diferença, mas é a principal diferença. Também é recomendável ter associações bidirecionais pelos documentos do Hibernate:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html

Especificamente:

Preferir associações bidirecionais: as associações unidirecionais são mais difíceis de consultar. Em um aplicativo grande, quase todas as associações devem ser navegáveis ​​nas duas direções nas consultas.

Pessoalmente, tenho um pequeno problema com esta recomendação geral - parece-me que há casos em que uma criança não tem nenhum motivo prático para saber sobre seus pais (por exemplo, por que um item de pedido precisa saber sobre o pedido que ele é associado a?), mas também vejo valor nele uma parte razoável do tempo. E como a bidirecionalidade realmente não prejudica nada, não acho muito censurável aderir.


A bidirecionalidade pode prejudicar em alguns casos, como o axtavt explicou! Eu teria muito cuidado com todas as relações mapeadas em uma entidade do Hibernate! Você pode acabar com o tempo infinito necessário para carregar coisas no Hibernate, a menos que saiba exatamente o que está fazendo. Pense com cuidado nos casos de uso e use diferentes classes de modelo para diferentes casos de uso. F.ex. em uma lista de coisas, não preciso de todos os objetos relacionados, apenas um rótulo e o ID. Portanto, a entidade da lista é diferente (e muito simples) da entidade de detalhes, para mim.
— cslotty
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.