programing

레이지 초기화graphql-spring에서 예외 발생

magicmemo 2023. 7. 15. 10:01
반응형

레이지 초기화graphql-spring에서 예외 발생

현재 REST-Server를 GraphQL로 마이그레이션하는 중입니다(적어도 부분적으로).대부분의 작업이 완료되었지만 해결할 수 없는 것처럼 보이는 이 문제를 우연히 발견했습니다. FetchType을 사용하는 graphql 쿼리에서 OneToMany 관계입니다.게으른.

저는 통합을 위해 https://github.com/graphql-java/graphql-spring-boot 과 https://github.com/graphql-java/graphql-java-tools 을 사용하고 있습니다.

다음은 예입니다.

엔티티:

@Entity
class Show {
   private Long id;
   private String name;

   @OneToMany(mappedBy = "show")
   private List<Competition> competition;
}

@Entity
class Competition {
   private Long id;
   private String name;

   @ManyToOne(fetch = FetchType.LAZY)
   private Show show;
}

스키마:

type Show {
    id: ID!
    name: String!
    competitions: [Competition]
}

type Competition {
    id: ID!
    name: String
}

extend type Query {
    shows : [Show]
}

해결 프로그램:

@Component
public class ShowResolver implements GraphQLQueryResolver {
    @Autowired    
    private ShowRepository showRepository;

    public List<Show> getShows() {
        return ((List<Show>)showRepository.findAll());
    }
}

이제 엔드포인트를 이 (바로 가기) 쿼리로 쿼리하는 경우:

{
  shows {
    id
    name
    competitions {
      id
    }
  }
}

이해합니다:

기관.기관.기관.레이지 초기화예외: 역할 컬렉션을 게으르게 초기화하지 못했습니다.Show.competitions, 프록시를 초기화할 수 없음 - 세션 없음

이제 이 오류가 발생하는 이유와 의미를 알 수 있지만, 이 오류에 대한 수정 사항을 적용해야 하는지는 잘 모르겠습니다.저는 모든 관계를 적극적으로 가져오기 위해 제 단체를 만들고 싶지 않습니다. 왜냐하면 그것은 GraphQL의 장점 중 일부를 부정할 것이기 때문입니다.해결책을 찾아야 할 만한 아이디어가 있습니까?감사합니다!

제가 선호하는 해결책은 서블릿이 응답을 보낼 때까지 트랜잭션을 열어두는 것입니다.이 작은 코드 변경으로 LazyLoad가 제대로 작동합니다.

import javax.servlet.Filter;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  /**
   * Register the {@link OpenEntityManagerInViewFilter} so that the
   * GraphQL-Servlet can handle lazy loads during execution.
   *
   * @return
   */
  @Bean
  public Filter OpenFilter() {
    return new OpenEntityManagerInViewFilter();
  }

}

나는 그것을 해결했고 graphql-java-tools 라이브러리의 설명서를 더 주의 깊게 읽었어야 했다고 생각합니다. GraphQLQueryResolver그것은 내가 또한 필요로 했던 기본적인 질문들을 해결합니다.GraphQLResolver<T>나로서는Show클래스, 다음과 같이 표시됩니다.

@Component
public class ShowResolver implements GraphQLResolver<Show> {
    @Autowired
    private CompetitionRepository competitionRepository;

    public List<Competition> competitions(Show show) {
        return ((List<Competition>)competitionRepository.findByShowId(show.getId()));
    }
}

라이브러리에서 내부의 복잡한 개체를 해결하는 방법을 알려줍니다.Show에서 " 클스이며, ▁the▁to▁include▁class▁requests다▁is▁the▁▁only▁and▁used 사용됩니다.Competition복 많이 !새해 복 많이 받으세요.

EDIT 31.07.2019: 저는 그 이후 아래의 해결책에서 벗어났습니다.트랜잭션을 장시간 실행하는 것은 좋은 방법이 아니며, 이 경우 응용프로그램을 확장하면 문제가 발생할 수 있습니다.우리는 비동기식으로 쿼리를 일괄 처리하기 위해 DataLoader를 구현하기 시작했습니다.DataLoaders의 비동기성과 함께 장시간 실행되는 트랜잭션은 교착 상태를 초래할 수 있습니다. https://github.com/graphql-java-kickstart/graphql-java-tools/issues/58#issuecomment-398761715 (자세한 내용은 위와 아래).아래 솔루션은 배치된 쿼리가 필요 없는 소규모 응용프로그램 및/또는 응용프로그램에 여전히 좋은 출발점이 될 수 있으므로 제거하지는 않겠지만, 이 의견을 염두에 두십시오.

편집: 요청하신 대로 사용자 지정 실행 전략을 사용하는 다른 솔루션이 있습니다.사용 중graphql-spring-boot-starter그리고.graphql-java-tools:

의 콩 ExecutionStrategy다음과 같이 트랜잭션을 처리합니다.

@Service(GraphQLWebAutoConfiguration.QUERY_EXECUTION_STRATEGY)
public class AsyncTransactionalExecutionStrategy extends AsyncExecutionStrategy {

    @Override
    @Transactional
    public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
        return super.execute(executionContext, parameters);
    }
}

이렇게 하면 쿼리의 전체 실행이 동일한 트랜잭션 내에 배치됩니다.이것이 가장 최적의 해결책인지, 그리고 오류 처리와 관련하여 이미 몇 가지 단점이 있지만, 당신은 그런 식으로 유형 해결책을 정의할 필요가 없습니다.

이 유일한 것이라면 하세요.ExecutionStrategy존재하는 콩, 이것은 콩 이름이 암시하는 것과는 반대로 돌연변이에도 사용될 것입니다.자세한 내용은 https://github.com/graphql-java-kickstart/graphql-spring-boot/blob/v11.1.0/graphql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/spring/web/boot/GraphQLWebAutoConfiguration.java#L161-L166 을 참조하십시오.이를 방지하려면 다른 항목을 정의합니다.ExecutionStrategy돌연변이에 사용:

@Bean(GraphQLWebAutoConfiguration.MUTATION_EXECUTION_STRATEGY)
public ExecutionStrategy queryExecutionStrategy() {
    return new AsyncSerialExecutionStrategy();
}

수락된 답변에 대해 혼란스러운 사람은 양방향 관계를 포함하도록 Java 엔티티를 변경하고 도우미 방법을 사용하여 다음을 추가해야 합니다.Competition그렇지 않으면 관계를 올바르게 설정하는 것을 잊기 쉽습니다.

@Entity
class Show {
   private Long id;
   private String name;

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "show")
   private List<Competition> competition;

   public void addCompetition(Competition c) {
      c.setShow(this);
      competition.add(c);
   }
}

@Entity
class Competition {
   private Long id;
   private String name;

   @ManyToOne(fetch = FetchType.LAZY)
   private Show show;
}

승인된 답변 뒤의 일반적인 직관은 다음과 같습니다.

graphql ShowResolver그러면 프로그램 목록을 가져오기 위해 트랜잭션이 열리지만 해당 작업이 완료되면 트랜잭션이 닫힙니다.

그다음다음대중한첩음리쿼에다런ql▁graphql▁nested▁for리에 대한 중첩 쿼리competitions를 호출하려고 .getCompetition()각각의 Show로, " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "LazyInitializationException거래가 종료되었기 때문입니다.

{
  shows {
    id
    name
    competitions {
      id
    }
  }
}

승인된 답변은 기본적으로 경기 목록을 검색하는 것을 우회하는 것입니다.OneToMany문제를 제거하는 새 트랜잭션에 새 쿼리를 만듭니다.

이게 해킹인지는 모르겠지만,@Transactional해결책에 대한 논리는 일리가 있지만 근본 원인을 확실히 이해하지 못합니다.

나를 위해 사용하는AsyncTransactionalExecutionStrategy 상태로 예: 게으른 이니시에이터 앱 수준 예외가 트랜잭션을 롤백 전용 상태로 트리거했습니다.은 전략의 .execute,발생시키는HttpRequestHandlerImpl400개의 빈 응답을 반환합니다.자세한 내용은 https://github.com/graphql-java-kickstart/graphql-java-servlet/issues/250 및 https://github.com/graphql-java/graphql-java/issues/1652 을 참조하십시오.

가 있었던 은 제게효있것은던을 사용한 입니다.Instrumentation전체 작업을 트랜잭션으로 마무리하려면: https://graph.chat/graphql/일반/부분적으로 스프링 포함~47749680-3bb7-4508-8935-1d20d04d0c6a

당신이 개체를 가져올 마다 쇼 개체의 모든 관련 경쟁을 원한다고 생각합니다.

기본적으로 엔티티의 모든 컬렉션 유형에 대한 가져오기 유형은 LAY입니다.최대 절전 모드가 컬렉션을 가져오도록 EARGER 유형을 지정할 수 있습니다.

Show 클래스에서 fetchType을 EAGER로 변경할 수 있습니다.

@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private List<Competition> competition;

당신은 당신의 해결자 클래스에 주석을 달기만 하면 됩니다.@Transactional그러면 저장소에서 반환된 엔티티는 데이터를 천천히 가져올 수 있습니다.

언급URL : https://stackoverflow.com/questions/48037601/lazyinitializationexception-with-graphql-spring

반응형