Estoy viendo problemas de rendimiento al recuperar varias instancias de objetos que tienen muchas relaciones con otros objetos. Estoy usando la implementación de JPA de Spring and Hibernate con MySQL. El problema es que al ejecutar una consulta JPA, Hibernate no se une automáticamente a otras tablas. Esto da como resultado n * r + 1 consultas SQL, donde n es la cantidad de objetos que se recuperan y r es el número de relaciones.¿Se puede utilizar Hibernate en aplicaciones sensibles al rendimiento?
ejemplo, una persona que vive en una dirección, tiene muchas aficiones, y ha visitado muchos países:
@Entity
public class Person {
@Id public Integer personId;
public String name;
@ManyToOne public Address address;
@ManyToMany public Set<Hobby> hobbies;
@ManyToMany public Set<Country> countriesVisited;
}
Al realizar una consulta JPA para obtener todas las personas nombradas Bob, y hay 100 Sacudidas en el base de datos:
SELECT p FROM Person p WHERE p.name='Bob'
Hibernate se traduce esto a 301 consultas SQL:
SELECT ... FROM Person WHERE name='Bob'
SELECT ... FROM Address WHERE personId=1
SELECT ... FROM Address WHERE personId=2
...
SELECT ... FROM Hobby WHERE personId=1
SELECT ... FROM Hobby WHERE personId=2
...
SELECT ... FROM Country WHERE personId=1
SELECT ... FROM Country WHERE personId=2
...
De acuerdo con las preguntas frecuentes de Hibernate (here y here), la solución es especificar LEFT JOIN o LEFT OUTER JOIN (for many-to-many) en la consulta. Así que ahora mi pregunta es así:
SELECT p, a, h, c FROM Person p
LEFT JOIN p.address a LEFT OUTER JOIN p.hobbies h LEFT OUTER JOIN p.countriesVisited c
WHERE p.name = 'Bob'
Esto funciona, pero no parece ser un error si hay más de un LEFT OUTER JOIN en cuyo caso Hibernate está buscando de forma incorrecta para una columna inexistente:
could not read column value from result set: personId69_2_; Column 'personId69_2_' not found.
Parece que el comportamiento de error posiblemente se solucione con Hibernate Core bug HHH-3636. Lamentablemente, la corrección no forma parte de ningún HAR de Hibernate lanzado. He ejecutado mi aplicación contra la compilación de instantáneas, pero el comportamiento de los errores aún está presente. También construí mi Hibernate Core JAR desde el último código en el repositorio y el comportamiento de los errores aún está presente. Entonces, tal vez HHH-3636 no aborda esto.
Esta limitación de rendimiento de Hibernate es muy frustrante. Si consulto 1000 objetos, se realizarán 1000 consultas SQL * 1 + en la base de datos. En mi caso tengo 8 relaciones, así que recibo 8001 consultas SQL, lo que resulta en un rendimiento horrible. La solución oficial de Hibernate para esto es unir todas las relaciones. Pero esto no es posible con más de una relación de muchos a muchos debido al comportamiento de los errores. Así que estoy atascado con combinaciones de la izquierda para las relaciones de muchos a uno y n * r + 1 consultas debido a las relaciones de muchos a muchos. Planeo enviar el problema LEFT OUTER JOIN como un error de Hibernate, pero mientras tanto, mi cliente necesita una aplicación que tenga un rendimiento razonable. Actualmente uso una combinación de búsqueda por lotes (BatchSize), ehcache y memoria caché personalizada en la memoria, pero el rendimiento es aún bastante pobre (mejoró la recuperación de 5000 objetos de 30 a 8 segundos). La conclusión es que demasiadas consultas SQL están llegando a la base de datos.
Entonces, mis preguntas, ¿es posible utilizar Hibernate en aplicaciones sensibles al rendimiento donde las tablas tienen relaciones múltiples entre sí? Me encantaría saber qué tan exitoso Hibernate usa el rendimiento de las direcciones. ¿Debo estar escribiendo SQL a mano (lo cual de alguna manera frustra el propósito de usar Hibernate)? ¿Debo normalizar mi esquema de base de datos para reducir el número de tablas unidas? ¿No debería utilizar Hibernate si necesito un rendimiento de consulta rápido? ¿Hay algo más rápido?
Hemos lanzado Batoo APP es ~ 15 veces más rápido Hibernate e implementa la especificación JPA% 100. La motivación principal del proyecto fue que las tres implementaciones de JPA simplemente se retrasaron. Habiendo comprendido que JPA podría ser mucho más rápido, hemos desarrollado Batoo JPA. Pruébalo http://batoo.jp –