带有graphql-spring的LazyInitializationException [英] LazyInitializationException with graphql-spring

查看:128
本文介绍了带有graphql-spring的LazyInitializationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在(至少部分地)将我的REST服务器迁移到GraphQL.大部分工作已经完成,但是我偶然发现了这个似乎无法解决的问题:使用FetchType.LAZY在graphql查询中建立了OneToMany关系.

I am currently in the middle of migrating my REST-Server to GraphQL (at least partly). Most of the work is done, but i stumbled upon this problem which i seem to be unable to solve: OneToMany relationships in a graphql query, with FetchType.LAZY.

我正在使用: https://github.com/graphql-java/graphql-spring-boot https://github.com/graphql-java/graphql-java-tools进行集成.

I am using: https://github.com/graphql-java/graphql-spring-boot and https://github.com/graphql-java/graphql-java-tools for the integration.

这里是一个例子:

实体:

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

如果我现在使用以下(速记)查询来查询端点:

If i now query the endpoint with this (shorthand) query:

{
  shows {
    id
    name
    competitions {
      id
    }
  }
}

我得到:

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

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: Show.competitions, could not initialize proxy - no Session

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

Now i know why this error happens and what it means, but i don't really know were to apply a fix for this. I don't want to make my entites to eagerly fetch all relations, because that would negate some of the advantages of GraphQL. Any ideas where i might need to look for a solution? Thanks!

推荐答案

我解决了这个问题,我应该更仔细地阅读graphql-java-tools库的文档. 除了可以解决基本查询的GraphQLQueryResolver之外,我的Show类还需要一个GraphQLResolver<T>,它看起来像这样:

I solved it and should have read the documentation of the graphql-java-tools library more carefully i suppose. Beside the GraphQLQueryResolver which resolves the basic queries i also needed a GraphQLResolver<T> for my Showclass, which looks like this:

@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对象时使用.新年快乐!

This tells the library how to resolve complex objects inside my Showclass and is only used if the initially query requests to include the Competitionobjects. Happy new Year!

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

EDIT 31.07.2019: I since stepped away from the solution below. Long running transactions are seldom a good idea and in this case it can cause problems once you scale your application. We started to implement DataLoaders to batch queries in an async matter. The long running transactions in combination with the async nature of the DataLoaders can lead to deadlocks: https://github.com/graphql-java-kickstart/graphql-java-tools/issues/58#issuecomment-398761715 (above and below for more information). I will not remove the solution below, because it might still be good starting point for smaller applications and/or applications which will not need any batched queries, but please keep this comment in mind when doing so.

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

As requested here is another solution using a custom execution strategy. I am using graphql-spring-boot-starter and graphql-java-tools:

我首先这样定义一个GraphQL Config:

I first define a GraphQL Config like this:

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

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

This puts the whole execution of the query inside the same transaction. I don't know if this is the most optimal solution, and it also already has some drawbacks in regards to error handling, but you don't need to define a type resolver that way.

这篇关于带有graphql-spring的LazyInitializationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆