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?

hguser
fonte
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:

axtavt
fonte
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 .
Vlad Mihalcea
fonte
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.

Constantino Cronemberger
fonte
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.

kvista
fonte
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