繁体   English   中英

Java GraphQL 模式中的 HashMap

[英]HashMap in Java GraphQL schema

我正在尝试创建一个 GraphQL Spring Boot 应用程序以在现有 REST Web API 之上创建一个 GraphQL 层,但我在处理架构中的 HashMap 时遇到了问题。

Timeline类有一个名为dataReference的字段,它是一个 HashMap。 我试图确定它在graphql模式类型列表DataReference这是一个键/值对,但我发现了以下错误:

Exception in thread "main" org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:155)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:540)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
    at com.foo.bar.graphql.AppLauncher.main(AppLauncher.java:43)
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:125)
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:86)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:414)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:174)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:152)
    ... 8 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'servletRegistrationBean' defined in com.foo.bar.graphql.AppLauncher: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.ServletRegistrationBean]: Factory method 'servletRegistrationBean' threw exception; nested exception is com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (ListType{type=TypeName{name='DataReference'}}) with java type (java.util.Map<java.lang.String, java.lang.String>): Java class is not a List or generic type information was lost: java.util.Map<java.lang.String, java.lang.String>
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:625)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:455)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
    at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:236)
    at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:224)
    at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:100)
    at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:88)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:250)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:237)
    at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:54)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5098)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1429)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
    at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
    at java.util.concurrent.FutureTask.run(FutureTask.java)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:944)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:839)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1429)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
    at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
    at java.util.concurrent.FutureTask.run(FutureTask.java)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:944)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:261)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:422)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:770)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.startup.Tomcat.start(Tomcat.java:370)
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:106)
    ... 13 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.ServletRegistrationBean]: Factory method 'servletRegistrationBean' threw exception; nested exception is com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (ListType{type=TypeName{name='DataReference'}}) with java type (java.util.Map<java.lang.String, java.lang.String>): Java class is not a List or generic type information was lost: java.util.Map<java.lang.String, java.lang.String>
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:620)
    ... 55 more
Caused by: com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (ListType{type=TypeName{name='DataReference'}}) with java type (java.util.Map<java.lang.String, java.lang.String>): Java class is not a List or generic type information was lost: java.util.Map<java.lang.String, java.lang.String>
    at com.coxautodev.graphql.tools.TypeClassMatcher.error(TypeClassMatcher.kt:22)
    at com.coxautodev.graphql.tools.TypeClassMatcher.match(TypeClassMatcher.kt:65)
    at com.coxautodev.graphql.tools.TypeClassMatcher.match(TypeClassMatcher.kt:28)
    at com.coxautodev.graphql.tools.SchemaClassScanner.scanResolverInfoForPotentialMatches(SchemaClassScanner.kt:215)
    at com.coxautodev.graphql.tools.SchemaClassScanner.scanQueueItemForPotentialMatches(SchemaClassScanner.kt:206)
    at com.coxautodev.graphql.tools.SchemaClassScanner.scanQueue(SchemaClassScanner.kt:103)
    at com.coxautodev.graphql.tools.SchemaClassScanner.scanForClasses(SchemaClassScanner.kt:81)
    at com.coxautodev.graphql.tools.SchemaParserBuilder.scan(SchemaParserBuilder.kt:149)
    at com.coxautodev.graphql.tools.SchemaParserBuilder.build(SchemaParserBuilder.kt:155)
    at com.foo.bar.graphql.AppLauncher.servletRegistrationBean(AppLauncher.java:55)
    at com.foo.bar.graphql.AppLauncher$$EnhancerBySpringCGLIB$$1ac15725.CGLIB$servletRegistrationBean$0(<generated>)
    at com.foo.bar.graphql.AppLauncher$$EnhancerBySpringCGLIB$$1ac15725$$FastClassBySpringCGLIB$$c7dabbfc.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
    at com.foo.bar.graphql.AppLauncher$$EnhancerBySpringCGLIB$$1ac15725.servletRegistrationBean(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    ... 56 more

这是我的架构文件:

extend type Query {
    getFoo: Timeline
}

type Timeline {
    dataReference: [DataReference]
}

type DataReference {
    key: String
    value: String
}

我的模型类:

package com.foo.bar.graphql.model;

import lombok.Getter;
import lombok.Setter;

import java.util.HashMap;
import java.util.Map;

@Getter @Setter
public class Timeline {
  private Map<String, String> dataReference = new HashMap<>();
}

我发现这个线程Return HashMap<String, Object> from GraphQL-Java关于在模式中定义映射的问题。 在接受的答案中,选项 1) 对我来说不可行,因为模型取自现有的 Web API 并且不能更改。 我对选项 2 的尝试已在上面列出,但没有奏效。 我还尝试了以下选项 3(使用标量):

架构:

extend type Query {
    getFoo: Timeline
}

scalar Object

type Timeline {
    dataReference: Object
}

Servlet 注册:

@Bean
    public ServletRegistrationBean servletRegistrationBean() {

        GraphQLSchema schema  = SchemaParser.newParser()
                        .resolvers(fooResolver)
                        .file("graphql/foo.graphqls")
                        .scalars(ExtendedScalars.Object)
                        .build()
                        .makeExecutableSchema();
        ExecutionStrategy executionStrategy = new AsyncExecutionStrategy();
        GraphQLServlet servlet = new SimpleGraphQLServlet(schema, executionStrategy);
        ServletRegistrationBean bean = new ServletRegistrationBean(servlet, "/graphql");
        return bean;
    }

pom.xml:

<dependency>
  <groupId>com.graphql-java</groupId>
  <artifactId>graphql-java-extended-scalars</artifactId>
  <version>1.0</version>
</dependency>

但是我收到了这个错误: Caused by: com.coxautodev.graphql.tools.SchemaClassScannerError: Expected a user-defined GraphQL scalar type with name 'Object' but found none!

如果有人可以帮助我使其工作,我更喜欢在模式中包含键/值对的方法。

最简单的方法是使用 DTO。 例如,使用像mapstruct这样的 DTO 映射器,将允许您自动映射除 Map 字段之外的所有字段,您将在其中应用转换。

你像这样创建一个 DTO:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TimelineDTO {

private List<DataReference> dataReference = new ArrayList();
}

拿到TimeLine对象后,将其映射到TimelineDTO,操作Map到List的转换。

List<DataReference> dataReferences = new ArrayList<>();
for (Map.Entry<String,String> entry : super.getDataReference().entrySet()) {

    dataReferences.add(new DataReference(entry.getKey(), entry.getValue()));

}

return dataReferences;

在您的 GraphQL 合同中,您改为返回 DTO:

 extend type Query {
    getFoo: TimelineDTO
}

type TimelineDTO {
    dataReference: [DataReference]
}

type DataReference {
    key: String
    value: String
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM