We are using graphql-spqr and graphql-spqr-spring-boot-starter for a new project (With Spring DataJPA, hibernate and so on).
We have a mutation like this:
@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;
}
This mutation works fine:
mutation createMatch ($matchForm: MatchFormInput!){
match: createMatch(matchForm: $matchForm) {
id
}
variables: {...}
I have ommitted the variables as they are not important. It does not work if I change it to:
mutation createMatch ($matchForm: MatchFormInput!){
match: createMatch(matchForm: $matchForm) {
id
homeTeam {
name
}
}
variables: {...}
I get a LazyInitalizationException and I know why:
The homeTeam is referenced by an ID and is loaded by teamRepository
. The returned team is only a hibernate proxy. Which is fine for saving the new Match, nothing more is needed. But for sending back the result GraphQL needs to access the proxy and calls match.team.getName(). But this happens obviously outside of the transaction marked with @Transactional
.
I can fix it with Team homeTeam teamRepository.findById(matchForm.getHomeId()).orElse(null); match.setHomeTeam(homeTeam);
Hibernate is no longer loading a proxy but the real object. But as I do not know what the GraphQL query exactly is asking for, it does not make sense to eagerly load all the data if it is not needed later on. It would be nice if GraphQL would be executed inside the @Transactional
, so I can define the transaction Boundaries for each query and mutation.
Any recommendation for this?
PS: I stripped the code and did some cleanup to make it more concise. so the code might not be runnable, but does illustrate the problem.