Dado el siguiente modelo de dominio, quiero cargar todos los correos Answer
electrónicos incluidos sus Value
correos electrónicos y sus respectivos hijos secundarios y ponerlos en un AnswerDTO
para luego convertirlos a JSON. Tengo una solución que funciona pero sufre del problema N + 1 del que quiero deshacerme usando un ad-hoc @EntityGraph
. Todas las asociaciones están configuradas LAZY
.
@Query("SELECT a FROM Answer a")
@EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Usando un ad-hoc @EntityGraph
en el Repository
método, puedo asegurarme de que los valores se obtienen previamente para evitar N + 1 en la Answer->Value
asociación. Si bien mi resultado está bien, hay otro problema de N + 1, debido a la carga lenta de la selected
asociación del MCValue
s.
Usando esto
@EntityGraph(attributePaths = {"value.selected"})
falla, porque el selected
campo es, por supuesto, solo parte de algunas de las Value
entidades:
Unable to locate Attribute with the the given name [selected] on this ManagedType [x.model.Value];
¿Cómo puedo decirle a JPA que solo intente buscar la selected
asociación en caso de que el valor sea a MCValue
? Necesito algo así optionalAttributePaths
.
fuente
selected
las respuestas que tienen unMCValue
. No me gustó que esto requeriría un bucle adicional y tendría que administrar la asignación entre los conjuntos de datos. Me gusta tu idea de explotar el caché de Hibernate para esto. ¿Puede explicar qué tan seguro (en términos de consistencia) es confiar en el caché para contener los resultados? ¿Funciona esto cuando las consultas se realizan en una transacción? Tengo miedo de los errores de inicialización diferidos esporádicos y difíciles de detectar.MCValue
entidades. Y no necesita un bucle adicional. Debe buscar todas lasMCValue
entidades con 1 consulta que se una a laAnswer
y use la misma cláusula WHERE que su consulta actual. También hablé sobre esto en la transmisión en vivo de hoy: youtu.be/70B9znTmi00?t=238 Comenzó a las 3:58 pero tomé algunas otras preguntas en el medio ...SINGLE_TABLE_INHERITANCE
.No sé qué está haciendo Spring-Data allí, pero para hacer eso, generalmente debe usar el
TREAT
operador para poder acceder a la subasociación, pero la implementación para ese Operador es bastante defectuosa. Hibernate admite acceso de propiedad de subtipo implícito, que es lo que necesitaría aquí, pero aparentemente Spring-Data no puede manejar esto correctamente. Puedo recomendar que eche un vistazo a Blaze-Persistence Entity-Views , una biblioteca que funciona sobre JPA que le permite mapear estructuras arbitrarias contra su modelo de entidad. Puede asignar su modelo DTO de forma segura, también la estructura de herencia. Las vistas de entidad para su caso de uso podrían verse asíCon la integración de datos de primavera proporcionada por Blaze-Persistence, puede definir un repositorio como este y usar directamente el resultado
Generará una consulta HQL que selecciona exactamente lo que asignó en el
AnswerDTO
cual es algo como lo siguiente.fuente
interface MCValueDTO extends ValueDTO { @Mapping("selected.id") Set<Long> getOption(); }
Mi último proyecto usó GraphQL (el primero para mí) y tuvimos un gran problema con las consultas N + 1 y tratando de optimizar las consultas para que solo se unan a las tablas cuando son necesarias. He encontrado que Cosium / spring-data-jpa-entity-graph es insustituible. Extiende
JpaRepository
y agrega métodos para pasar un gráfico de entidad a la consulta. Luego puede crear gráficos de entidades dinámicas en tiempo de ejecución para agregar uniones izquierdas solo para los datos que necesita.Nuestro flujo de datos se parece a esto:
Para resolver el problema de no incluir nodos no válidos en el gráfico de entidad (por ejemplo,
__typename
desde graphql), creé una clase de utilidad que maneja la generación del gráfico de entidad. La clase que llama pasa el nombre de la clase para la que genera el gráfico, que luego valida cada nodo en el gráfico contra el metamodelo mantenido por el ORM. Si el nodo no está en el modelo, lo elimina de la lista de nodos del gráfico. (Esta verificación debe ser recursiva y también verificar a cada niño)Antes de encontrar esto, probé las proyecciones y cualquier otra alternativa recomendada en los documentos Spring JPA / Hibernate, pero nada parecía resolver el problema de manera elegante o al menos con un montón de código adicional.
fuente
selected
asociación no está disponible para todos los subtipos devalue
.Editado después de tu comentario:
Pido disculpas, no he entendido bien su problema en la primera ronda, su problema ocurre al inicio de spring-data, no solo cuando intenta llamar a findAll ().
Entonces, ahora puede navegar, el ejemplo completo se puede extraer de mi github: https://github.com/bdzzaid/stackoverflow-java/blob/master/jpa-hibernate/
Puede reproducir y solucionar su problema fácilmente dentro de este proyecto.
Efectivamente, los datos de Spring y la hibernación no son capaces de determinar el gráfico "seleccionado" de forma predeterminada y debe especificar la forma de recopilar la opción seleccionada.
Primero, debes declarar los NamedEntityGraphs de la clase Respuesta
Como puede ver, hay dos NamedEntityGraph para el valor de atributo de la clase Respuesta
El primero para todo Valor sin relación específica para cargar
El segundo para el valor específico de Multichoice . Si elimina este, reproduce la excepción.
En segundo lugar, debe estar en un contexto transaccional answerRepository.findAll () si desea obtener datos en tipo LAZY
fuente
value
deAnswer
sino obtener laselected
asociación en caso de quevalue
sea aMCValue
. Su respuesta no incluye ninguna información al respecto.OneToMany
comoFetchType.EAGER
pero como se indica en la pregunta: todas las asociaciones sonLAZY
.selected
para cada respuesta en lugar de cargarlas por adelantado.