小编典典

带有graphql-spring的LazyInitializationException

spring-boot

我目前正在将REST服务器迁移到GraphQL(至少部分地)。大多数工作已经完成,但是我偶然发现了这个似乎无法解决的问题:使用FetchType.LAZY在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
    }
  }
}

我得到:

org.hibernate.LazyInitializationException:无法延迟初始化角色集合:Show.competitions,无法初始化代理-
没有会话

现在我知道为什么会发生此错误及其含义,但是我真的不知道要为此应用修复程序。我不想让我的实体急于获取所有关系,因为那样会否定GraphQL的某些优势。我可能需要寻找解决方案的任何想法吗?谢谢!


阅读 304

收藏
2020-05-30

共1个答案

小编典典

我解决了它,我应该更仔细地阅读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类中的复杂对象,并且仅在最初查询请求包含Competition对象时才使用。新年快乐!

编辑31.07.2019
:从那以后我就离开了下面的解决方案。长时间运行的事务很少是一个好主意,在这种情况下,一旦扩展应用程序,可能会导致问题。我们开始实现DataLoader以在异步事件中批处理查询。长时间运行的事务与DataLoader的异步性质相结合,可能导致死锁:https :
//github.com/graphql-java-kickstart/graphql-java-
tools/issues/58#issuecomment-398761715(针对以下内容,更多信息)。我不会删除下面的解决方案,因为对于较小的应用程序和/或不需要任何批处理查询的应用程序,它仍然可能是一个很好的起点,但是在执行此操作时请记住此注释。

编辑: 根据要求,这里是使用自定义执行策略的另一种解决方案。我正在使用graphql-spring-boot-startergraphql- java-tools

我首先定义一个GraphQL Config,如下所示:

@Configuration
public class GraphQLConfig {
    @Bean
    public Map<String, ExecutionStrategy> executionStrategies() {
        Map<String, ExecutionStrategy> executionStrategyMap = new HashMap<>();
        executionStrategyMap.put("queryExecutionStrategy", new AsyncTransactionalExecutionStrategy());
        return executionStrategyMap;
    }
}

其中AsyncTransactionalExecutionStrategy的定义如下:

@Service
public class AsyncTransactionalExecutionStrategy extends AsyncExecutionStrategy {

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

这将整个查询执行置于同一事务中。我不知道这是否是最佳的解决方案,并且在错误处理方面也已经存在一些缺点,但是您不必以这种方式定义类型解析器。

2020-05-30