java hibernate jpa lazy-loading java-ee-7

java - Cargue el gráfico de objetos recursivos sin N+1 producto cartesiano con JPA e Hibernate



lazy-loading java-ee-7 (1)

Al convertir un proyecto de Ibatis a JPA 2.1, me enfrento a un problema en el que tengo que cargar un gráfico de objetos completo para un conjunto de objetos, sin tocar las selecciones N + 1 o usar productos cartesianos por razones de rendimiento.

Una consulta de los usuarios producirá una <Tarea> Lista, y debo asegurarme de que cuando devuelvo las tareas, tengan todas las propiedades completadas, incluidos los principales , los secundarios , las dependencias y las propiedades . Primero déjame explicar los dos objetos de entidad involucrados.

Una tarea es parte de una jerarquía. Puede tener una tarea principal y también puede tener hijos. Una tarea puede depender de otras tareas, expresada por la propiedad ''dependencias''. Una tarea puede tener muchas propiedades, expresadas por la propiedad properties .

Los objetos de ejemplo se han simplificado todo lo posible y se elimina el código de repetición.

@Entity public class Task { @Id private Long id; @ManyToOne(fetch = LAZY) private Task parent; @ManyToOne(fetch = LAZY) private Task root; @OneToMany(mappedBy = "task") private List<TaskProperty> properties; @ManyToMany @JoinTable(name = "task_dependency", inverseJoinColumns = { @JoinColumn(name = "depends_on")}) private List<Task> dependencies; @OneToMany(mappedBy = "parent") private List<Task> children; } @Entity public class TaskPropertyValue { @Id private Long id; @ManyToOne(fetch = LAZY) private Task task; private String name; private String value; }

La jerarquía de tareas para una tarea determinada puede ser infinitamente profunda, por lo que para facilitar la obtención de todo el gráfico, una tarea tendrá un puntero a su tarea raíz a través de la propiedad "raíz".

En Ibatis, simplemente obtuve todas las Tareas para la lista distinta de ID de raíz, y luego hice consultas ad-hoc para todas las propiedades y dependencias con una consulta "task_id IN ()". Cuando tuve esos, usé el código Java para agregar propiedades, hijos y dependencias a todos los objetos del modelo para que el gráfico estuviera completo. Para una lista de tareas de cualquier tamaño, solo haría 3 consultas SQL y estoy tratando de hacer lo mismo con JPA. Dado que la propiedad ''padre'' indica dónde agregar los hijos, ni siquiera tuve que consultarlos.

He intentado diferentes enfoques, incluyendo:

Deja que la carga perezosa haga su trabajo

  • Rendimiento suicida, sin necesidad de elaborar :)

ÚNETE a FETCH hijos, ÚNETE a FETCH dependencias, ÚNETE FETCH propiedades

  • Esto es problemático porque los productos cartesianos resultantes son enormes, y mi implementación de JPA (Hibernate) no es compatible con la Lista, solo se establece cuando se buscan varias bolsas. Una tarea puede tener un gran número de propiedades, haciendo que los productos cartesianos sean ineficaces.

Consultas ad-hoc de la misma manera que hice en ibatis.

  • No puedo agregar elementos secundarios, dependencias y propiedades a las colecciones inicializadas perezosas en los objetos de Tarea, porque Hibernate intentará agregarlos como objetos nuevos.

Una posible solución podría ser crear nuevos objetos de Tarea que no sean administrados por JPA y unir mi jerarquía usando esos, y creo que puedo vivir con eso, pero no me siento muy "JPA", y luego no pude. use JPA para lo que es bueno: seguimiento y cambios persistentes en mis objetos automáticamente.

Cualquier consejo sería muy apreciado. Estoy abierto a usar extensiones específicas de proveedores si es necesario. Estoy corriendo en Wildfly 8.1.0.Final (perfil completo de Java EE7) con Hibernate 4.3.5.Final.


Opciones Disponibles

Hay algunas estrategias para alcanzar tus metas:

  • la búsqueda de sub-selección cargaría todas las entidades perezosas con una sub-selección adicional, la primera vez que necesite una asociación perezosa de ese tipo dado. Este sonido es atractivo al principio, pero hace que su aplicación sea frágil en cuanto al número de entidades de subselección adicionales que se pueden obtener y puede propagarse a otros métodos de servicio.

  • la obtención de lotes es más fácil de controlar, ya que puede imponer la cantidad de entidades que se cargarán en un lote y podría no afectar demasiados otros casos de uso.

  • utilizando una expresión de tabla común recursiva si su db lo admite.

Planear con anticipación

Al final, se trata de lo que planea hacer con las filas seleccionadas. Si solo se trata de mostrarlos en una vista, una consulta nativa es más que suficiente .

Si necesita conservar las entidades en varias solicitudes (primero la parte de vista, la segunda para la parte de actualización) las entidades son un mejor enfoque.

Por su respuesta, veo que necesita emitir un EntityManager.merge() y probablemente confiar en la cascada para propagar las transiciones de estado de los niños (agregar / eliminar).

Ya que estamos hablando de 3 consultas de JPA, y siempre y cuando no obtenga un Producto Cartesiano, debería estar bien con JPA.

Conclusión

Debe esforzarse por la cantidad mínima de consultas, pero eso no significa que siempre tendrá que tener una y solo una consulta. Dos o tres consultas no son un problema en absoluto.

Siempre que controle el número de consultas y no entre en un problema de consulta de N + 1, también estará bien con más de una consulta. Intercambiar un producto cartesiano (2 búsquedas de uno a muchos) por una combinación y una selección adicional es una buena oferta de todos modos.

Al final, siempre debe verificar el plan de consulta EXPLAIN ANALYZE y reforzar / repensar su estrategia.