Estamos utilizando graphql-SPQR y graphql-SPQR-primavera-arranque-motor de arranque para un nuevo proyecto (Con la primavera DataJPA, hibernación y así sucesivamente).
Tenemos una mutación de esta manera:
@Transactional
@GraphQLMutation
public Match createMatch(@NotNull @Valid MatchForm matchForm) {
Match match = new Match(matchForm.getDate());
match.setHomeTeam(teamRepository.getOne(matchForm.getHomeId()));
match.setAwayTeam(teamRepository.getOne(matchForm.getAwayId()));
match.setResult(matchForm.getResult());
matchRepository.save(match);
return match;
}
Esta mutación funciona bien:
mutation createMatch ($matchForm: MatchFormInput!){
match: createMatch(matchForm: $matchForm) {
id
}
variables: {...}
He omitido las variables, ya que no son importantes. No funciona si la cambio a:
mutation createMatch ($matchForm: MatchFormInput!){
match: createMatch(matchForm: $matchForm) {
id
homeTeam {
name
}
}
variables: {...}
Consigo un LazyInitalizationException y sé por qué:
El Hometeam es referenciado por un ID y es cargado por teamRepository
. El equipo devuelto es sólo un indicador de hibernación. Lo cual está muy bien para guardar el nuevo ajuste, nada se necesita más. Pero para devolver el resultado GraphQL necesita para acceder al proxy y llama match.team.getName (). Pero esto sucede, obviamente, fuera de la transacción marcada con @Transactional
.
Lo puedo arreglar con el equipo hometeam teamRepository.findById (matchForm.getHomeId ()) OrElse (nulo).; match.setHomeTeam (Hometeam);
Hibernate ya no está cargando un proxy, pero el objeto real. Pero como yo no sé lo que la consulta GraphQL exactamente está pidiendo, no tiene sentido cargar con entusiasmo todos los datos si no se necesita más adelante. Sería bueno si GraphQL sería ejecutado dentro de la @Transactional
, por lo que puede definir los límites de transacción para cada consulta y la mutación.
Cualquier recomendación para esto?
PD: Me quitó el código e hizo algo de limpieza para que sea más concisa. por lo que el código no sea ejecutable, pero sirve para ilustrar el problema.
Puedo venir con un par de cosas que usted puede tener en cuenta.
1) de carga con impaciencia como sea necesario
Siempre se puede comprobar qué campos preventivamente las necesidades de consulta y con entusiasmo cargarlos. Los detalles, así explican en las Fetchers de datos eficiente de construcción con la vista puesta artículo en el blog graphql-java.
En resumen, se puede obtener la llamada DataFetchingEnvironment#getSelectionSet()
que le dará DataFetchingFieldSelectionSet
y que contiene toda la información que necesita para optimizar la carga.
En SPQR, siempre se puede obtener una bodega de DataFetchingEnvironment
(y mucho más) mediante la inyección ResolutionEnvironment
:
@GraphQLMutation
public Match createMatch(
@NotNull @Valid MatchForm matchForm,
@GraphQLEnvironment ResolutionEnvironment env) { ... }
Si sólo necesita los nombres de los subcampos primero nivel, puede inyectarse
@GraphQLEnvironment Set<String> selection
en lugar.
Para la capacidad de prueba, siempre se puede cablear en su propia ArgumentInjector
que cherry-picks exactamente lo que necesita inyectarse, por lo que es más fácil burlarse de pruebas.
2) Ejecutar toda la consulta de resolución GraphQL en una transacción
En lugar de o además de tener @Transactional
en resolutores individuales, se puede sustituir el controlador por defecto con uno que recorre toda la cosa en una transacción. Sólo una palmada @Transactional
en el método controlador, y ya está bueno para ir.