¿Cuál es la diferencia entre las asociaciones unidireccionales y bidireccionales JPA e Hibernate?

135

¿Cuál es la diferencia entre las asociaciones unidireccionales y bidireccionales?

Dado que la tabla generada en la base de datos es la misma, la única diferencia que encontré es que cada lado de las asociaciones bidireccionales tendrá una referencia al otro, y el unidireccional no.

Esta es una asociación unidireccional

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

La asociación bidireccional

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

La diferencia es si el grupo tiene una referencia del usuario.

¿Entonces me pregunto si esta es la única diferencia? cual se recomienda

Hguser
fuente
77
El grupo ahora sabrá qué usuarios contiene. No creo que esto sea de ninguna manera una pequeña diferencia.
Satadru Biswas
55
Las relaciones bidireccionales se convirtieron en un caos para mí a la hora de actualizar. :)
diyoda_

Respuestas:

152

La diferencia principal es que la relación bidireccional proporciona acceso de navegación en ambas direcciones, para que pueda acceder al otro lado sin consultas explícitas. También le permite aplicar opciones en cascada a ambas direcciones.

Tenga en cuenta que el acceso a la navegación no siempre es bueno, especialmente para las relaciones "uno a muchos" y "muchos a muchos". Imagine un Groupque contiene miles de Users:

  • ¿Cómo accederías a ellos? Con tantos correos Userelectrónicos, por lo general, debe aplicar algo de filtrado y / o paginación, de modo que debe ejecutar una consulta de todos modos (a menos que use el filtrado de recopilación , que para mí parece un truco). Algunos desarrolladores pueden tender a aplicar filtros en la memoria en tales casos, lo que obviamente no es bueno para el rendimiento. Tenga en cuenta que tener una relación de este tipo puede alentar a este tipo de desarrolladores a usarla sin tener en cuenta las implicaciones de rendimiento.

  • ¿Cómo agregarías nuevos correos Userelectrónicos al Group? Afortunadamente, Hibernate observa el lado propietario de la relación cuando la persiste, por lo que solo puede establecerla User.group. Sin embargo, si desea mantener los objetos en la memoria constante, también es necesario añadir Usera Group.users. ¡Pero haría que Hibernate obtenga todos los elementos de Group.usersla base de datos!

Por lo tanto, no puedo estar de acuerdo con la recomendación de las mejores prácticas . Debe diseñar relaciones bidireccionales con cuidado, considerando los casos de uso (¿necesita acceso de navegación en ambas direcciones?) Y las posibles implicaciones de rendimiento.

Ver también:

axtavt
fuente
Hola, gracias, parece que eres un experto en hibernación, ya que has respondido a mi pregunta: stackoverflow.com/questions/5350770/… . Y ahora no estoy seguro de que la relación se mantenga, ya que no puedo escribir más en el comentario, así que lo publico aquí dpaste.de/J85m . Tenga un cheque si es posible. :)
hguser
@hguser: Si finalmente decide hacer que la relación sea bidireccional, creo que sería mejor llamar setGroup()desde addUser()el fin de mantener ambos lados consistente.
axtavt
¿Qué tal si no llamo a setGroup () dentro de group.addUser ()?
hguser
@hguser: La relación no sería persistente, ver el punto 2 en la respuesta. Puede llamar setGroup()sin addUser(), pero daría como resultado un estado inconsistente de los objetos en la memoria.
axtavt
Descubrí que no puedo hacer un mapeo correcto, ¿puedes dedicar algo de tiempo a revisar mi proyecto en github? Es un pequeño proyecto.
hguser
31

Hay dos diferencias principales.

Accediendo a los lados de la asociación

El primero está relacionado con cómo accederá a la relación. Para una asociación unidireccional, puede navegar por la asociación solo desde un extremo.

Entonces, para una @ManyToOneasociación unidireccional , significa que solo puede acceder a la relación desde el lado secundario donde reside la clave externa.

Si tiene una @OneToManyasociación unidireccional , significa que solo puede acceder a la relación desde el lado primario donde reside la clave externa.

Para la @OneToManyasociación bidireccional , puede navegar por la asociación en ambos sentidos, ya sea desde el lado primario o secundario.

También debe usar métodos de utilidad de agregar / quitar para asociaciones bidireccionales para asegurarse de que ambos lados estén sincronizados correctamente .

Actuación

El segundo aspecto está relacionado con el rendimiento.

  1. Para @OneToMany, asociaciones unidireccionales no funcionan tan bien como los bidireccionales .
  2. Para @OneToOne, una asociación bidireccional hará que el padre se busque ansiosamente si Hibernate no puede determinar si se debe asignar el Proxy o un valor nulo .
  3. Para @ManyToMany, el tipo de colección hace una gran diferencia ya que Setsfunciona mejor queLists .
Vlad Mihalcea
fuente
11

En términos de codificación, una relación bidireccional es más compleja de implementar porque la aplicación es responsable de mantener ambas partes sincronizadas de acuerdo con la especificación 5 de JPA (en la página 42). Desafortunadamente, el ejemplo dado en la especificación no da más detalles, por lo que no da una idea del nivel de complejidad.

Cuando no se utiliza un caché de segundo nivel, generalmente no es un problema no tener los métodos de relación implementados correctamente porque las instancias se descartan al final de la transacción.

Cuando se usa caché de segundo nivel, si algo se corrompe debido a métodos de manejo de relaciones implementados incorrectamente, esto significa que otras transacciones también verán los elementos corruptos (el caché de segundo nivel es global).

Una relación bidireccional implementada correctamente puede hacer que las consultas y el código sean más simples, pero no debe usarse si realmente no tiene sentido en términos de lógica empresarial.

Constantino Cronemberger
fuente
9

No estoy 100% seguro de que esta sea la única diferencia, pero es la principal diferencia. También se recomienda tener asociaciones bidireccionales por los documentos de Hibernate:

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

Específicamente:

Prefiere asociaciones bidireccionales: las asociaciones unidireccionales son más difíciles de consultar. En una aplicación grande, casi todas las asociaciones deben ser navegables en ambas direcciones en consultas.

Personalmente, tengo un pequeño problema con esta recomendación general: me parece que hay casos en los que un niño no tiene ninguna razón práctica para saber acerca de su padre (por ejemplo, ¿por qué un artículo de pedido necesita saber sobre el pedido? asociado con?), pero también veo valor en él una parte razonable del tiempo. Y dado que la bidireccionalidad realmente no hace daño a nada, no me parece demasiado objetable cumplirla.

kvista
fuente
¡La bidireccionalidad puede doler en algunos casos, como ha explicado axtavt! ¡Tendría mucho cuidado con cada relación asignada en una entidad de Hibernate! Puede terminar con el tiempo infinito necesario para cargar cosas en Hibernate, a menos que sepa exactamente lo que está haciendo. Piense detenidamente en los casos de uso y use diferentes clases de modelos para diferentes casos de uso. F.ex. en una lista de cosas, no necesito todos los objetos relacionados, solo una etiqueta y la ID. Entonces, la entidad de la lista es diferente (y muy simple) que la entidad de detalle, para mí.
cslotty