GraphQL 介绍
GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。
基于node的服务端开发中,GraphQL技术较为成熟常用,在基于Java
的服务端开发中,由于国内对该API标准的了解程度不高,以及引入GraphQL可能需要维护两份重复数据(schema和相应java代码实现)。
开始
本文旨在从Java服务端开发的角度,介绍GraphQL的落地实践。根据官网的示例:GraphQL Java和Spring Boot入门 ,遗憾是使用Gradle
构建。本文使用Maven方式构建SpringBoot
的一般方式。根据官网的示例,基本能了解一些执行方式。
使用依赖为本文发布时间最新版本v14,另外使用mvc
做url
地址映射/graphql
作为请求入口。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>14.0</version>
</dependency>
</dependencies>
GraphQL 实例
官网推荐SDL
的写法,也可以使用Java语句编写,如果你喜欢。?*.graphql
的文件放置在src/main/resources
目录下,通过读取文件并对其进行解析。通过引用声明Bean实例 GraphQL
到全局,就完成数据查询构建。
总体上,创建GraphQL和GraphQLSchema实例的过程如下所示:
RuntimeWiring
内编写执行函数数据返回DataFetcher
类型,进行解析选择字段的函数。
/**
* 输出world
* qurey { hello }
* @return 返回字符串
*/
public DataFetcher getHelloWorldDataFetcher() {
return dataFetchingEnvironment -> "world";
}
主要编写代码如下:
@Bean
public GraphQL graphQL() throws IOException {
// SDL读取查询类型文件,new SchemaParser().parse(?)解析File、InputStream、String
// ClassPathResource classPathResource = new ClassPathResource("schema.graphql");
// TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(classPathResource.getInputStream());
// 多SDL文件注册
// ClassPathResource UserSchema = new ClassPathResource("schema/UserSchema.graphql");
// ClassPathResource schema = new ClassPathResource("schema/QuerySchema.graphql");
// TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
// SchemaParser schemaParser = new SchemaParser();
// typeRegistry.merge(schemaParser.parse(UserSchema.getInputStream()));
// typeRegistry.merge(schemaParser.parse(schema.getInputStream()));
// 遍历解析目录下的schema,没找到直接获取文件列表的方法
TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
SchemaParser schemaParser = new SchemaParser();
String[] schemaArr = {"UserSchema", "QuerySchema", "MutationSchema"};
for (String str : schemaArr) {
typeRegistry.merge(schemaParser.parse(new ClassPathResource("schema/" + str + ".graphql").getInputStream()));
}
RuntimeWiring runtimeWiring = buildWiring(); // 数据方法对应编写
SchemaGenerator schemaGenerator = new SchemaGenerator(); // 查询器构建
// 查询生成
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
return GraphQL.newGraphQL(graphQLSchema).build();
}
/**
* 数据类型方法对应编写
*
* @return RuntimeWiring对应执行方法
*/
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
// 查询方法R/get
.type("Query", builderFunction -> builderFunction
.dataFetcher("hello", graphQLDataFetchers.getHelloWorldDataFetcher())
.dataFetcher("echo", graphQLDataFetchers.getEchoDataFetcher())
.dataFetcher("users", userDataFetcher.getUsersDataFetcher())
.dataFetcher("user", userDataFetcher.getUserByIdDataFetcher())
)
// 级联字段关联查询
.type("User", builderFunction -> builderFunction.dataFetcher("info", userDataFetcher.getInfoDataFetcher()))
// 增改删方法CUD/post/put/del
.type("Mutation", builderFunction -> builderFunction
.dataFetcher("createUser", userDataFetcher.createUserDataFetcher())
.dataFetcher("updateUser", userDataFetcher.updateUserDataFetcher())
.dataFetcher("deleteUser", userDataFetcher.deleteUserDataFetcher())
)
.build();
}
执行方法
在controller中编写URL入口/graphql
,进行支持get
、post
两种请求方式,将得到的参数数据进行处理后,使用graphQL执行查询器。可能很多人想处理错误和json输出的,请阅读官方对执行的更多操作方法。
/**
* 执行graphQL查询
*
* @param query 查询语句-类json字符
* @param operationName 查询操作名称-默认空字符
* @param variables 查询参数变量-map对象、默认为空map
* @return map对象
*/
private Map<String, Object> executeGraphqlQuery(String query, String operationName, Map<String, Object> variables) {
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.query(query)
.operationName(operationName)
.variables(variables)
.build();
return graphql.execute(executionInput).toSpecification();
}
最后
示例项目使用springboot
与maven
方式构建,微服务采用restful
和graphql
两种方式进行开发,两者相辅相成,比如:上传、websocket等一些接口混用的模式。
示例代码:GitHub
springboot
构建版本:2.2.2.RELEASE
,建议改为你目前使用的版本,避免再次下载。