¿Qué controla exactamente la estrategia de recuperación de JPA? No puedo detectar ninguna diferencia entre impaciente y vago. En ambos casos, JPA / Hibernate no une automáticamente relaciones de varios a uno.
Ejemplo: la persona tiene una sola dirección. Una dirección puede pertenecer a muchas personas. Las clases de entidad anotadas de JPA se ven así:
@Entity
public class Person {
@Id
public Integer id;
public String name;
@ManyToOne(fetch=FetchType.LAZY or EAGER)
public Address address;
}
@Entity
public class Address {
@Id
public Integer id;
public String name;
}
Si uso la consulta JPA:
select p from Person p where ...
JPA / Hibernate genera una consulta SQL para seleccionar de la tabla Person, y luego una consulta de dirección distinta para cada persona:
select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3
Esto es muy malo para conjuntos de resultados grandes. Si hay 1000 personas, genera 1001 consultas (1 de Persona y 1000 distintas de Dirección). Lo sé porque estoy mirando el registro de consultas de MySQL. Tenía entendido que establecer el tipo de búsqueda de la dirección en ansioso hará que JPA / Hibernate consulte automáticamente con una combinación. Sin embargo, independientemente del tipo de búsqueda, todavía genera consultas distintas para las relaciones.
Solo cuando le digo explícitamente que se una, realmente se une:
select p, a from Person p left join p.address a where ...
¿Me estoy perdiendo de algo? Ahora tengo que codificar manualmente cada consulta para que se una a las relaciones de muchos a uno. Estoy usando la implementación JPA de Hibernate con MySQL.
Editar: Parece (consulte las preguntas frecuentes de Hibernate aquí y aquí ) que FetchType
no afecta las consultas de JPA. Entonces, en mi caso, le he dicho explícitamente que se una.
Respuestas:
JPA no proporciona ninguna especificación sobre las anotaciones de mapeo para seleccionar la estrategia de búsqueda. En general, las entidades relacionadas se pueden recuperar de cualquiera de las formas que se indican a continuación.
Entonces
SELECT
yJOIN
son dos extremos ySUBSELECT
se encuentra en el medio. Uno puede elegir la estrategia adecuada en función de su modelo de dominio.De forma predeterminada,
SELECT
lo utilizan tanto JPA / EclipseLink como Hibernate. Esto se puede anular usando:en hibernación. También permite configurar el
SELECT
modo explícitamente usando el@Fetch(FetchMode.SELECT)
cual se puede ajustar usando el tamaño del lote, por ejemplo@BatchSize(size=10)
.Las anotaciones correspondientes en EclipseLink son:
fuente
"mxc" es correcto.
fetchType
solo especifica cuándo se debe resolver la relación.Para optimizar la carga ansiosa mediante el uso de una combinación externa, debe agregar
a tu campo. Esta es una anotación específica de hibernación.
fuente
El atributo fetchType controla si el campo anotado se recupera inmediatamente cuando se recupera la entidad principal. No necesariamente dicta cómo se construye la declaración de recuperación, la implementación real de sql depende del proveedor que esté utilizando toplink / hibernate, etc.
Si establece
fetchType=EAGER
Esto significa que el campo anotado se completa con sus valores al mismo tiempo que los demás campos de la entidad. Entonces, si abre un entitymanager, recupera sus objetos de persona y luego cierra el entitymanager, posteriormente, hacer una person.address no dará como resultado una excepción de carga diferida.Si establece,
fetchType=LAZY
el campo solo se rellena cuando se accede a él. Si ha cerrado el entitymanager para entonces, se lanzará una excepción de carga diferida si hace una person.address. Para cargar el campo, debe volver a poner la entidad en un contexto de entitymangers con em.merge (), luego hacer el acceso al campo y luego cerrar el entitymanager.Es posible que desee una carga diferida al construir una clase de cliente con una colección para pedidos de clientes. Si recuperó todos los pedidos de un cliente cuando deseaba obtener una lista de clientes, esta operación de base de datos puede ser costosa cuando solo busca el nombre del cliente y los detalles de contacto. Es mejor dejar el acceso a la base de datos para más tarde.
Para la segunda parte de la pregunta: ¿cómo hacer que hibernar genere SQL optimizado?
Hibernate debería permitirle proporcionar sugerencias sobre cómo construir la consulta más eficiente, pero sospecho que hay algo mal en la construcción de su tabla. ¿La relación se establece en las tablas? Es posible que Hibernate haya decidido que una consulta simple será más rápida que una combinación, especialmente si faltan índices, etc.
fuente
Prueba con:
Me funciona de manera similar con JPA2 / EclipseLink, pero parece que esta característica también está presente en JPA1 :
fuente
Si usa EclipseLink en lugar de Hibernate, puede optimizar sus consultas mediante "sugerencias de consulta". Consulte este artículo de la Wiki de Eclipse: EclipseLink / Examples / JPA / QueryOptimization .
Hay un capítulo sobre "Lectura conjunta".
fuente
para unirse puede hacer varias cosas (usando eclipselink)
en jpql puede hacer la búsqueda de unión izquierda
en una consulta con nombre, puede especificar una sugerencia de consulta
en TypedQuery puedes decir algo como
query.setHint("eclipselink.join-fetch", "e.projects.milestones");
también hay una sugerencia de recuperación por lotes
query.setHint("eclipselink.batch", "e.address");
ver
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
fuente
Tuve exactamente este problema con la excepción de que la clase Person tenía una clase de clave incrustada. Mi propia solución fue unirlos en la consulta Y eliminar
@Fetch(FetchMode.JOIN)
Mi clase de identificación incrustada:
fuente
Se me ocurren dos cosas.
Primero, ¿estás seguro de que te refieres a ManyToOne como dirección? Eso significa que varias personas tendrán la misma dirección. Si se edita para uno de ellos, se editará para todos. ¿Esa es tu intención? El 99% de las veces las direcciones son "privadas" (en el sentido de que pertenecen a una sola persona).
En segundo lugar, ¿tiene alguna otra relación ansiosa en la entidad Persona? Si mal no recuerdo, Hibernate solo puede manejar una relación ansiosa en una entidad, pero esa es posiblemente información desactualizada.
Digo eso porque su comprensión de cómo debería funcionar esto es esencialmente correcta desde donde estoy sentado.
fuente