当前位置: 首页 > 知识库问答 >
问题:

带有Spring Boot和事务边界的graphql spqr

车明贤
2023-03-14

我们正在将图形ql-spqr和图形ql-spqr-Spring启动器用于一个新项目(使用Spring数据JPA,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,我知道原因:

家庭团队由一个ID引用,并由teamRepository加载。返回的团队只是一个Hibernate代理。这对于保存新的匹配很好,不再需要任何东西。但是要返回结果,GraphQL需要访问代理并调用match.team.getName()。但是这显然发生在标有@Transactional的事务之外。

我可以用团队主页团队存储库.找到ById(匹配形式.getHomeId()).或Else(空)来修复它;匹配设置首页团队(家庭团队);

Hibernate不再加载代理,而是加载真正的对象。但是,由于我不知道GraphQL查询到底需要什么,如果以后不需要,那么急切地加载所有数据是没有意义的。如果GraphSQL能够在@Transactional中执行,那就更好了,这样我就可以为每个查询和变异定义事务边界。

对此有什么建议吗?

PS:我剥离了代码并做了一些清理以使其更简洁。所以代码可能无法运行,但确实说明了问题。

共有2个答案

壤驷坚
2023-03-14

我可以带来一些你可能需要考虑的事情。

1) 根据需要紧急加载

您始终可以先发制人地检查查询所需的字段并急切地加载它们。细节在graphql-java博客的“通过查看未来”文章中构建高效的数据提取器中得到了很好的解释。

简而言之,您可以获得调用“数据取迁环境#getSelectionSet()”,它将为您提供“数据取迁字段选择集”,其中包含优化加载所需的所有信息。

在SPQR中,您总是可以通过注入< code > resolution environment 来获得< code > DataFetchingEnvironment (以及更多内容):

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

如果只需要第一级子字段的名称,可以插入

@GraphQLEnvironment Set<String> selection

相反。

对于可测试性,您可以始终连接自己的ArgumentInjector,cherry会精确地选择您需要注入的内容,因此在测试中模拟更容易。

2) 在事务中运行整个GraphQL查询解析

除了在单个解析器上使用< code>@Transactional之外,还可以用一个在事务中运行全部内容的控制器来代替默认控制器。只需将< code>@Transactional添加到控制器方法中,就可以开始了。

江凯风
2023-03-14

@kaqqao答案非常好,但我想评论这些并展示第三种解决方案:

>

  • 我不喜欢这个解决方案,因为我必须在我真的不关心它的环境中做很多工作。这不是图形QL的内容。如果需要,应进行解决。在每个 GraphQL 查询和路径的每个方向上展望未来对我来说都很奇怪。

    我设法用服务类实现了这一点。但您会遇到异常问题,因为异常被包装并且没有得到正确处理。

    public class TransactionalGraphQLExecutor implements GraphQLServletExecutor {
     private final ServletContextFactory contextFactory;
     @Autowired(required = false)
     @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
     private DataLoaderRegistryFactory dataLoaderRegistryFactory;
    
     private final TxGraphQLExecutor txGraphQLExecutor;
    
     public TransactionalGraphQLExecutor(ServletContextFactory contextFactory, TxGraphQLExecutor txGraphQLExecutor) {
         this.contextFactory = contextFactory;
         this.txGraphQLExecutor = txGraphQLExecutor;
     }
    
     @Override
     public Map<String, Object> execute(GraphQL graphQL, GraphQLRequest graphQLRequest, NativeWebRequest nativeRequest) {
         ExecutionInput executionInput = buildInput(graphQLRequest, nativeRequest, contextFactory, dataLoaderRegistryFactory);
         if (graphQLRequest.getQuery().startsWith("mutation")) {
             return txGraphQLExecutor.executeReadWrite(graphQL, executionInput);
         } else {
             return txGraphQLExecutor.executeReadOnly(graphQL, executionInput);
         }
     }
    

    }

    public class TxGraphQLExecutor  { 
     @Transactional
     public Map<String, Object> executeReadWrite(GraphQL graphQL, ExecutionInput executionInput) {
         return graphQL.execute(executionInput).toSpecification();
     }
    
     @Transactional(readOnly = true)
     public Map<String, Object> executeReadOnly(GraphQL graphQL, ExecutionInput executionInput) {
         return graphQL.execute(executionInput).toSpecification();
     }
    

    }

    也有可能使用仪器,请参见https://spectrum.chat/graphql/general/transactional-queries-with-spring~47749680-3bb7-4508-8935-1d20d04d0c6a

    目前我最喜欢的是有另一个手动解析器

     @GraphQLQuery
     @Transactional(readOnly = true)
     public Team getHomeTeam(@GraphQLContext Match match) {
         return matchRepository.getOne(match.getId()).getHomeTeam();
     }
    

    当然,您也可以设置spring.jpa。在视图中打开=false(反模式)

    或者你可以急切地去拿。

    如果能用GraphQL定义事务边界就好了。

  •  类似资料:
    • 问题内容: 我正在尝试通过以下示例来消除我对Spring Transaction边界的怀疑。 我想将test2()方法与test1()隔离开,即每次调用test()时,test2()都不应读取test1()提交的数据。请建议是否可以使用传播或隔离属性来处理这种情况。 提前致谢。 问题答案: 事务属性应用于外部调用,而不是由bean方法(例如您的案例)进行的内部调用。如果要将事务边界应用于调用,则应

    • 我正在使用spring boot和hibernate over jpa以及tomcat连接池。您能帮助我理解spring在事务期间如何使用DB连接吗。例如,考虑以下场景: 我们有2个连接的DB连接池 Spring启动一个事务,即用@Transactional注释修饰的调用方法 此方法执行数据库更新 呼叫外部服务 当从外部服务接收到响应时,它更新DB并返回 Spring提交事务 假设外部服务(步骤4

    • 我有一个关于ApacheCamel的问题。我无法找到多播是否被事务处理。如果它被事务处理,事务是如何实现的?事务边界是什么?

    • 我需要一点帮助在C#中设置一个HTTP Post。我感谢任何预先得到的帮助。 在这里使用小提琴手是我的原始帖子: 我的要求有点棘手。他们需要一个带有边界的多部分柱。我不熟悉设置边界。如果有人能帮忙,我将不胜感激。 以下是我的要求: 所以我认为这就是POST应该看起来的样子,但是我需要一些关于我的C#的帮助。 这是我用来创建网络请求的C#代码。

    • 我正在自己配置Spring Boot应用程序,以运行两个数据库(两个transactionManager相同)。MariaDB和MongoDb。在@Repository中,我已经用@PersistenceContext使用了@Autowired,注释@Transactional正在正确地使用TransactionManager。但对我来说,最有用的是在@Services层上添加@Transacio

    • 问题内容: 我有以下CSS: 添加边框半径:5px似乎没有任何作用,我认为这是因为我使用的是边框渐变,我是否有办法完全实现所需的5px边框半径? 问题答案: You cannot use with gradient. Here is another idea where you can rely on multiple background and adjust the : 如果需要透明性,可以考