2010-09-03 31 views
10

¿Tiene JPA 2 algún mecanismo para ejecutar consultas recursivas?¿Consulta recursiva de JPA?

Aquí está mi situación: Tengo una entidad E, que contiene un campo entero x. También puede tener hijos de tipo E, mapeados a través de @OneToMany. Lo que me gustaría hacer es encontrar una E por clave primaria, y obtener su valor de x, junto con los valores x de todos sus descendientes. ¿Hay alguna manera de hacer esto en una sola consulta?

Estoy usando Hibernate 3.5.3, pero preferiría no tener ninguna dependencia explícita en las API de Hibernate.


EDIT: Según this artículo, Hibernate no tienen esta característica, o al menos no lo hizo en marzo. Por lo tanto, parece poco probable que JPA lo tenga, pero me gustaría asegurarme.

Respuesta

21

Usando el modelo de adyacencia simple donde cada fila contiene una referencia a sus padres que hará referencia a otra fila en la misma tabla no coopera bien con JPA. Esto se debe a que JPA no tiene soporte para generar consultas utilizando la cláusula Oracle CONNECT BY o la declaración WITH estándar de SQL. Sin cualquiera de esas 2 cláusulas, no es realmente posible hacer útil el Modelo de Adyacencia.

Sin embargo, hay un par de otros enfoques para modelar este problema que pueden aplicarse a este problema. El primero es Modelo de ruta materializada. Aquí es donde la ruta completa al nodo se aplana en una sola columna. La definición de la tabla se extiende de este modo:

CREATE TABLE node (id INTEGER, 
        path VARCHAR, 
        parent_id INTEGER REFERENCES node(id)); 

Para insertar un árbol de nodos se ve algo como:

INSERT INTO node VALUES (1, '1', NULL); -- Root Node 
INSERT INTO node VALUES (2, '1.2', 1); -- 1st Child of '1' 
INSERT INTO node VALUES (3, '1.3', 1); -- 2nd Child of '1' 
INSERT INTO node VALUES (4, '1.3.4', 3); -- Child of '3' 

Así que para obtener Nodo '1' y todos sus hijos la consulta es:

SELECT * FROM node WHERE id = 1 OR path LIKE '1.%'; 

Para asignar esto a JPA solo haga que la columna 'ruta' sea un atributo de su objeto persistente. Sin embargo, tendrá que hacer la contabilidad para mantener el campo 'ruta' actualizado. JPA/Hibernate no hará esto por usted. P.ej. si mueve el nodo a un elemento primario diferente, deberá actualizar la referencia principal y determinar el nuevo valor de ruta desde el nuevo objeto principal.

El otro enfoque se llama Conjunto anidado modelo, que es un poco más complejo. Probablemente sea el mejor described por su creador (en lugar de ser agregado textualmente por mí).

Hay un tercer enfoque llamado Modelo de intervalo anidado, sin embargo, esto tiene una gran dependencia de los procedimientos almacenados para implementar.

Una explicación mucho más completa de este problema se describe en el capítulo 7 de The Art of SQL.

+0

Esto es bastante más complejo de lo que estaba buscando, pero gracias por la respuesta informativa. También hay una alternativa más simple al modelo de ruta materializada: haga que cada nodo almacene la ID del nodo raíz del árbol al que pertenece. Esto significa que solo puede usar el nodo raíz como punto de partida de la consulta, pero en mi caso específico eso no es un problema, así que probablemente sea eso lo que haré. –

+0

Puede usar listas de adyacencia, administradas a través de JPA, pero use una consulta nativa para usar funciones SQL recursivas. Sería un poco asqueroso, y no portátil, pero sería algo así como lo mejor de ambos mundos. Los conjuntos anidados son portátiles, pero complicados, y no funcionan muy bien para algunos tipos de consultas bastante comunes. –

+0

Ha cambiado algo con JPA 2.0 ... Por ejemplo, he usado una NamedQuery nativa para tener una cláusula 'CONNECT BY' en mi consulta. Lo he intentado y devuelve a los niños, pero no en una lista jerárquica ... Solo niños planos. ¿Algunas ideas? – SoftwareSavant

4

La mejor respuesta en esta publicación me parece un truco de trabajo masivo. Ya he tenido que lidiar con modelos de datos en los que brillantes ingenieros decidieron que sería una buena idea codificar Tree Hiarchies en campos DB como texto como "Europe | Uk | Shop1 | John" y con volúmenes masivos de datos en estas tablas . No sorprendentemente, el rendimiento de la consulta de la forma MyHackedTreeField LIKE 'parentHierharchy%' donde los asesinos. Enfrentarse a este tipo de problema en última instancia requiere crear en memoria caché de las jerarquías de árbol y tantos otros ...

Si necesita ejecutar una consulta recursiva y su volumen de datos no es masivo ... simplifique su vida y simplemente cargue los campos DB que necesita para ejecutar su plan. Y codifica tu recursión en java. No lo haga en el DB a menos que tenga una buena razón para hacerlo.

E incluso si el volumen de datos que tiene es masivo, lo más probable es que pueda subdividir su problema en lotes de árboles recursivos independidos y procesarlos uno por vez sin necesidad de cargar todos los datos a la vez.

Cuestiones relacionadas