graphql-spqr с Spring Boot и транзакционной границей

Мы используем graphql-spqr и graphql-spqr-spring-boot-starter для нового проекта (с Spring DataJPA, hibernate и так далее).

У нас есть такая мутация:

@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;
}

Эта мутация работает нормально:

mutation createMatch ($matchForm: MatchFormInput!){   
  match: createMatch(matchForm: $matchForm) {       
    id
}
variables: {...}

Я опустил переменные, поскольку они не важны. Это не работает, если я изменю это на:

mutation createMatch ($matchForm: MatchFormInput!){   
  match: createMatch(matchForm: $matchForm) {       
    id
    homeTeam {
       name
    }
 }
variables: {...}

Я получаю LazyInitalizationException и знаю почему:

На homeTeam ссылается идентификатор и загружает teamRepository . Возвращенная команда - это только прокси в спящем режиме. Что хорошо для сохранения нового матча, больше ничего не нужно. Но для отправки результата GraphQL необходимо получить доступ к прокси и вызвать match.team.getName (). Но это происходит явно за пределами транзакции, помеченной @Transactional .

Я могу это исправить с помощью Team homeTeam teamRepository.findById (matchForm.getHomeId ()). OrElse (null); match.setHomeTeam (homeTeam);

Hibernate больше не загружает прокси, а реальный объект. Но поскольку я не знаю, что именно запрашивает запрос GraphQL, не имеет смысла загружать все данные, если в дальнейшем они не нужны. Было бы хорошо, если бы GraphQL выполнялся внутри @Transactional , поэтому я могу определить границы транзакции для каждого запроса и мутации.

Любая рекомендация для этого?

PS: я удалил код и сделал некоторую очистку, чтобы сделать его более кратким. поэтому код не может быть запущен, но иллюстрирует проблему.

Всего 1 ответ


Я могу прийти с парой вещей, которые вы можете рассмотреть.

1) С готовностью загрузить по мере необходимости

Вы всегда можете предварительно проверить, какие поля запрашивает запрос, и охотно их загрузить. Детали также объяснены в Построении эффективных сборщиков данных, просматривая статью в блоге graphql-java.

Вкратце, вы можете получить вызов DataFetchingEnvironment#getSelectionSet() который даст вам DataFetchingFieldSelectionSet и который содержит всю информацию, необходимую для оптимизации загрузки.

В SPQR вы всегда можете получить DataFetchingEnvironment (и многое другое), внедрив ResolutionEnvironment :

@GraphQLMutation
public Match createMatch(
           @NotNull @Valid MatchForm matchForm,
           @GraphQLEnvironment ResolutionEnvironment env) { ... }

Если вам просто нужны имена подполей 1-го уровня, вы можете ввести

@GraphQLEnvironment Set<String> selection

вместо.

Для тестируемости вы всегда можете подключить свой собственный ArgumentInjector который выбирает именно то, что вам нужно, чтобы его легче было смоделировать в тестах.

2) Выполнить полное разрешение запроса GraphQL в транзакции

Вместо или в дополнение к наличию @Transactional для отдельных распознавателей, вы можете заменить контроллер по умолчанию на тот, который выполняет всю вещь в транзакции. Просто @Transactional к методу контроллера, и все @Transactional .