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?
Respostas:
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
Group
que contém milhares deUser
s:Como você os acessaria? Com tantos
User
s, 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
User
s aoGroup
? Felizmente, o Hibernate olha para o lado proprietário do relacionamento ao persistir, para que você possa configurá-lo apenasUser.group
. No entanto, se você deseja manter os objetos na memória consistentes, também precisa adicionarUser
aGroup.users
. Mas isso faria o Hibernate buscar todos os elementos doGroup.users
banco 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:
fonte
setGroup()
deaddUser()
para manter os dois lados consistentes.setGroup()
semaddUser()
, mas isso resultaria em um estado inconsistente de objetos na memória.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
@ManyToOne
associação unidirecional , isso significa que você só pode acessar o relacionamento do lado filho em que a chave estrangeira reside.Se você tiver uma
@OneToMany
associação unidirecional , isso significa que você só pode acessar o relacionamento do lado pai onde reside a chave estrangeira.Para a
@OneToMany
associaçã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.
@OneToMany
, associações unidirecionais não executar, bem como aqueles bidirecionais .@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 .@ManyToMany
, o tipo de coleção faz muita diferença, poisSets
apresenta um desempenho melhor queLists
.fonte
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.
fonte
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:
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.
fonte