Spring boot+graphql

一、使用graphql-java-tools方式

<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.6.0</version>
</dependency> <dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>5.0.4</version>
</dependency>

schema.graphqls

type Query {
books: [Book!]
} type Book {
id: Int!
name: String!
author: Author!
} type Author {
id: Int!
name: String!
}

对应的java class

class Book {
private int id;
private String name;
private int authorId; // constructor // getId
// getName
// getAuthorId
} class Author {
private int id;
private String name; // constructor // getId
// getName
}

Book-Resolver

class BookResolver implements GraphQLResolver<Book> {

    private AuthorRepository authorRepository;

    public BookResolver(AuthorRepository authorRepository) {
this.authorRepository = authorRepository;
} public Author author(Book book) {
return authorRepository.findById(book.getAuthorId());
}
}

Query-Resolver

class Query implements GraphQLQueryResolver {

    private BookRepository bookRepository;

    public Query(BookRepository bookRepository) {
this.bookRepository = bookRepository;
} public List<Book> books() {
return bookRepository.findAll();
}
}

Type Query 没有对应的java class,如果type 中的所有字段和java class成员变量一致,则该type可以不用定义Resolver.

graphql type中的字段映射Java class字段的优先级

对于graphql objectType中的字段映射为java class字段顺序如下:

  1. method (*fieldArgs [, DataFetchingEnvironment])
  2. method is(*fieldArgs [, DataFetchingEnvironment]), only if the field returns a Boolean
  3. method get(*fieldArgs [, DataFetchingEnvironment])
  4. method getField(*fieldArgs [, DataFetchingEnvironment])
  5. field

例如:

上述type Book中的name字段的映射顺序为:

  1. 在java Book类中找name(参数)方法。
  2. 如果Book类中没有name(参数)方法,则继续找isName(参数)方法。
  3. 如果Book中没有isName(参数)方法,则继续在Book中找getName(参数)方法。
  4. 如果Book中没有getName()方法,则继续在Book中找getFieldName(参数)方法。
  5. 如果Book中没有getFieldName(参数)方法,在继续在Book中找name成员变量。
  6. 如果Book中没有name成员变量,则报错。

graphql type中的字段映射Resolver的优先级:

  1. method (dataClassInstance, *fieldArgs [, DataFetchingEnvironment])
  2. method is(dataClassInstance, *fieldArgs [, DataFetchingEnvironment]), only if the field returns a Boolean
  3. method get(dataClassInstance, *fieldArgs [, DataFetchingEnvironment])
  4. method getField(dataClassInstance, *fieldArgs [, DataFetchingEnvironment])

注:Resolver的映射优先级高于Java Class,首先在Resolver中查找,如果没找到,才会在Java class中查找 :例如上述type Book中的author字段,会首先映射为BookResolver重的author(Book)方法。

解析schema.graphqls,创建Graphql对象:

import com.coxautodev.graphql.tools.SchemaParser;

GraphQLSchema schema = SchemaParser.newParser().file("schema.graphqls")
.resolvers(new QueryResolver(), new BookResolver())
.build()
.makeExecutableSchema(); GraphQL graphQL = GraphQL.newGraphQL(schema).build();
// 执行查询
ExecutionResult result = graphQL.execute(query); Map<String, Object> map = result.toSpecification();

二、不使用Resolver

schema.graphqls:

type Query {
bookById(id: ID): Book
} type Book {
id: ID
name: String
pageCount: Int
author: Author
} type Author {
id: ID
firstName: String
lastName: String
}

加载schema.graphqls,创建GraphQL对象:

import graphql.schema.idl.SchemaParser;

@Value("classpath:schema.graphqls")
Resource resource; @PostConstruct
private void loadSchema() throws Exception {
File schemaFile = resource.getFile(); GraphQLSchema schema = buildSchema(schemaFile); graphQL = GraphQL.newGraphQL(schema).build();
} private GraphQLSchema buildSchema(File file) throws Exception {
TypeDefinitionRegistry registry = new SchemaParser().parse(file);
RuntimeWiring runtimeWiring = buildWiring();
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
} private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
// 为每个graphql type的字段提供DataFetcher
.type(newTypeWiring("Query")
.dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
.type(newTypeWiring("Book")
.dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
.build();
}

DataFetcher:

@Component
public class GraphQLDataFetchers { private static List<Map<String, String>> books = Arrays.asList(
ImmutableMap.of("id", "book-1",
"name", "Harry Potter and the Philosopher's Stone",
"pageCount", "223",
"authorId", "author-1"),
ImmutableMap.of("id", "book-2",
"name", "Moby Dick",
"pageCount", "635",
"authorId", "author-2"),
ImmutableMap.of("id", "book-3",
"name", "Interview with the vampire",
"pageCount", "371",
"authorId", "author-3")
); private static List<Map<String, String>> authors = Arrays.asList(
ImmutableMap.of("id", "author-1",
"firstName", "Joanne",
"lastName", "Rowling"),
ImmutableMap.of("id", "author-2",
"firstName", "Herman",
"lastName", "Melville"),
ImmutableMap.of("id", "author-3",
"firstName", "Anne",
"lastName", "Rice")
); public DataFetcher getBookByIdDataFetcher() {
return dataFetchingEnvironment -> {
String bookId = dataFetchingEnvironment.getArgument("id");
return books
.stream()
.filter(book -> book.get("id").equals(bookId))
.findFirst()
.orElse(null);
};
} public DataFetcher getAuthorDataFetcher() {
return dataFetchingEnvironment -> {
Map<String,String> book = dataFetchingEnvironment.getSource();
String authorId = book.get("authorId");
return authors
.stream()
.filter(author -> author.get("id").equals(authorId))
.findFirst()
.orElse(null);
};
}
}

注:这种方式,并不要求一定要提供type对应的java class,只要在对应的DataFetcher中返回符合type的数据格式即可

三、方式三,不使用graphql-java-tools

不定义schema.graphqls,以编码的方式创建graphql type。

定义graphql type:

GraphQLObjectType fooType = newObject()
.name("Foo")
.field(newFieldDefinition()
.name("bar")
.type(GraphQLString))
.build();

上述代码相当于使用schema方式创建了如下graphql type:

type Foo {
bar: String
}

为类型的field指定DataFetcher:

DataFetcher<Foo> fooDataFetcher = environment -> {
// environment.getSource() is the value of the surrounding
// object. In this case described by objectType
Foo value = perhapsFromDatabase(); // Perhaps getting from a DB or whatever
return value;
} GraphQLObjectType objectType = newObject()
.name("ObjectType")
.field(newFieldDefinition()
.name("foo")
.type(GraphQLString)
.dataFetcher(fooDataFetcher))
.build();

完整的代码:

// 定义一个type Query
/**
* 相当于 type QueryType{ hello: String }
*/
GraphQLObjectType queryType = newObject()
.name("QueryType")
.field(newFieldDefinition()
.name("hello")
.type(GraphQLString)
.dataFetcher(new StaticDataFetcher("world!"))
.build();
// 创建GraphQLSchema
GraphQLSchema schema = GraphQLSchema.newSchema()
.query(queryType)
.build(); // Make the schema executable
GraphQL executor = GraphQL.newGraphQL(graphQLSchema).build();
ExecutionResult executionResult = executor.execute("{hello}");

四、参考链接

https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/

https://www.graphql-java-kickstart.com/tools/

https://www.graphql-java.com/documentation/v11/

最新文章

  1. 419. Battleships in a Board
  2. F1 分数
  3. gitlb gerrit jenkins CI整合调试
  4. Java安全编码之用户输入
  5. POJ 2309 BST 树状数组基本操作
  6. 关于Windows 7的64位系统不兼容某些控件的问题
  7. win8 64位操作系统 Microsoft Visual Studio 2010在IIS上调试 “此任务要求应用程序具有提升的权限”等问题
  8. Spring、控制反转与依赖注入(概念)
  9. mysql 视图示例
  10. js加强版图片轮播
  11. Jquery实现选项卡效果
  12. 阿里巴巴Java开发规约插件地址
  13. 20160210.CCPP体系详解(0020天)
  14. python2 with open(path,&quot;&quot;,) as f:
  15. 生命短暂,意识到开始使用python的重要性,python3.5.3安装和使用篇
  16. Android Studio--》Gradle Scripts配置说明
  17. 《剑指offer》-链表的第一个公共节点
  18. Eclipse 重构
  19. MySql安装完成后,Navicat连接不上的问题
  20. 使用apt-mirror搭建debian本地仓库

热门文章

  1. Thread.currentThread().getName() ,对象实例.getName() 和 this.getName()区别
  2. LINUX 笔记5
  3. laravel-excel文档翻译笔记
  4. /etc/inittab加入自动启动格式
  5. 2018.08.10 atcoder No Need(线性dp)
  6. yii2 控制器的生命周期
  7. hdu-1131(卡特兰数+大数)
  8. &#39;org.springframework.web.filter.CharacterEncodingFilter&#39; is not assignable to &#39;javax.servlet.Filter,This inspection lets you spot the following problems that might occur in descriptors that are used t
  9. select自定义下拉选择图标
  10. HDU 2844 Coins (多重背包问题DP)