2010-01-14 10 views
5

Tengo problemas para representar una jerarquía de objetos en Hibernate. He buscado y no he podido encontrar ningún ejemplo que lo haga o algo similar. Me disculpan si se trata de una pregunta común.Representación eficiente de jerarquías en Hibernate

Tengo dos tipos que me gustaría persistir usando Hibernate: Grupos y Artículos.
* Los grupos se identifican únicamente por una combinación de su nombre y su elemento primario.
* Los grupos están organizados en varios árboles, de modo que cada grupo tiene cero o un grupo principal.
* Cada elemento puede ser miembro de cero o más grupos.

Idealmente, me gustaría una relación bidireccional que permite que consiga:
* todos los Grupos que un artículo es un miembro de
* Todos los artículos que son un miembro de un grupo en particular o sus descendientes.
También necesito poder recorrer el árbol de grupos desde la parte superior para mostrarlo en la interfaz de usuario.

La estructura básica de objetos lo ideal sería tener este aspecto:

Originalmente, que acababa de hacer un simple bidireccional muchos-a-muchos relación entre artículos y grupos, de manera que ir a buscar todos los elementos de una jerarquía del grupo requiere recursión hacia abajo del árbol, y recuperar los grupos de un elemento era un captador simple, es decir:

class Group { 
    ... 
    private Set<Item> items; 
    private Set<Group> children; 
    ... 
    /** @return all items in this group and its descendants */ 
    Set<Item> getAllItems() { 
     Set<Item> allItems = new HashSet<Item>(); 
     allItems.addAll(this.items); 
     for(Group child : this.getChildren()) { 
      allItems.addAll(child.getAllItems()); 
     } 
     return allItems; 
    } 

    /** @return all direct children of this group */ 
    Set<Group> getChildren() { 
     return this.children; 
    } 
    ... 
} 

class Item { 
    ... 
    private Set<Group> groups; 
    /** @return all groups that this Item is a direct member of */ 
    Set<Group> getGroups() { 
     return this.groups; 
    } 
    ... 
} 

Sin embargo, esto dio lugar a múltiples peticiones de base de datos para buscar a los elementos de un grupo con muchos descendientes, o para recuperar todo el grupo árbol para mostrar en la interfaz de usuario. Esto parece muy ineficiente, especialmente con árboles grupales más profundos y grandes. ¿Existe alguna forma mejor o estándar de representar esta relación en Hibernate?

¿Estoy haciendo algo obviamente incorrecto o estúpido?


Mi único otro pensamiento hasta el momento era la siguiente: Reemplazar campos id, los padres y el nombre del grupo con un "camino" única cadena que especifica toda la ascendencia de un grupo, por ejemplo:
/rootGroup
/rootGroup/aChild
/rootGroup/aChild/aGrandChild

La tabla de unión entre Grupos y Elementos contendría entonces group_path y item_id.

Esto soluciona inmediatamente los dos problemas que estaba sufriendo anteriormente:
1. Toda la jerarquía de grupos puede extraerse de la base de datos en una sola consulta y reconstruirse en la memoria.
2. Para recuperar todos los elementos de un grupo o de sus descendientes, podemos seleccionar entre group_item donde group_path = 'N' o group_path como 'N /%'

Sin embargo, esto parece derrotar el punto de utilizar Hibernate. Todos los pensamientos bienvenidos!

Respuesta

4

Creo que la verdadera pregunta es dónde quieres que golpee el rendimiento. Si declaras perezosas todas tus relaciones y extraes lo que necesitas, distribuyes el golpe con el tiempo. En muchos casos, esto significa que su usuario (persona o silicio) percibe un tiempo de reacción más rápido.Incluso si procesa todo el gráfico de una vez, en realidad no necesita todo el gráfico en la memoria al mismo tiempo.

Por otro lado, al tirar todo el gráfico, el resultado se acelera por adelantado mientras se manipula más rápido.

La mayor diferencia entre los dos es su caso de uso específico. Si este es un servidor web, tirando todo el gráfico en la memoria matará al servidor. La mayoría de las personas no pueden manejar más de 7-10 opciones de todos modos, y todo el gráfico puede abrumar fácilmente al usuario con opciones que dificultan la navegación de la interfaz de usuario.

recordar también estas reglas de optimización:

  1. ¿No
  2. En serio, no lo hagas. Es más económico lanzar el hardware a un problema de rendimiento que optimizar el código hasta el punto en que ya no se puede mantener.
  3. Si cree que debe hacerlo, busque el cuello de botella, utilizando una herramienta de creación de perfiles, y soluciónelo.
+1

¿No es la máxima de Knuth "La optimización prematura es la raíz de todos los males?" –

+0

Supongo que, como en varios casos similares, este no es un problema de grandes estructuras de datos en memoria, sino un número creciente de operaciones de SQL. Si bien cada operación es realmente rápida, en redes de objeto complejas, el número de consultas y la latencia de la comunicación generarán una aplicación para rastrear. En este caso, normalmente prefiero identificar partes más grandes de la red y usar técnicas para (pre) buscarlas lo más temprano posible (vea la respuesta adicional a continuación). La memoria puede reutilizarse fácilmente. –

+0

@Mathew Sí, creo que sí dijo eso. @Ralf Simplemente no tenemos la información. Solo estoy tratando de cubrir ambas bases. –

3

En mis proyectos anteriores he implementado dos soluciones diferentes pero comparables a este problema.

La solución más importante (desde un punto de vista del rendimiento) se basó en el hecho de que tenía muchos árboles diferentes de tamaño moderado. En lugar de mapear todas las hojas de los árboles mediante relaciones bidireccionales, cada una de ellas sale de una relación con la raíz del árbol y una segunda con su padre directo.

Usando esta estrategia fue bastante fácil identificar todos los nodos raíz (FROM tree t WHERE t.parent IS NULL) y fue muy fácil buscar el árbol completo basado en el elemento raíz (FROM tree t WHERE t.root =: raíz).

La relación "Establecer hijos" se declaró un atributo @Transient. Utilizando una operación de utilidad simple, fue bastante fácil reconstruir la estructura de árbol original en cualquier momento.

La otra solución se refiere a un pequeño número de estructuras de árbol realmente grandes. Pero, afortunadamente, estas estructuras tenían una profundidad limitada. En lugar de usar solo una raíz, he mapeado varios (6 si mal no recuerdo) los niveles de "padres". De esta forma, fue fácil buscar y reconstruir subárboles completos de la base de datos.

(Esas level1 a las relaciones Level6 fueron asignadas como relaciones "perezosos muchos-a-uno" y la aplicación fue fuertemente dependend en las memorias caché de 2º nivel.)

+0

Hola Ralf, gracias por la respuesta. Este enfoque parece que definitivamente aceleraría las cosas en ciertos casos, pero no estoy seguro si es aplicable a todos los casos de uso. – Armand

2

Look en conjuntos anidados. He aquí un buen artículo:

http://www.developersdex.com/gurus/articles/112.asp

Básicamente, se sacrifica algo de rendimiento cuando la jerarquía se modifica con el fin de simplificar las recuperaciones de profundidad, al igual que lo que quiere hacer con artículos como miembros de grupos de descendientes.

+0

muy interesante! Sin embargo, creo que tendré que volver a leerlo varias veces antes de obtenerlo por completo. – Armand

+0

Feliz de ayudar. Han pasado un par de años desde que trabajé con estos, pero si tienes preguntas específicas. Después de volver a leer el artículo al que me he vinculado, me di cuenta de que había revisado algunos detalles. Aquí hay un artículo más explicativo, courtsey de MySQL: http://dev.mysql.com/tech-resources/articles/hierarchical-data.html Cubren conjuntos anidados a mitad de camino. – Taylor

1

Tengo las mismas preocupaciones sobre el alto costo de sql que causa el algoritmo recursivo durante la obtención de los elementos secundarios de un nodo. He decidido crear una nueva tabla en la base de datos que tiene una relación de muchos a uno con la tabla original e inserte una clave externa de la identificación del nodo. Y la siguiente columna es la identificación del niño. Con esta estructura, he persistido en la estructura del árbol una vez en una tabla de base de datos. De modo que no hay necesidad de obtener nodos secundarios recursivamente.Acabo de escribir una consulta SQL que une dos tablas y me da los elementos secundarios de un nodo en particular.

Cuestiones relacionadas