GraphQL
GraphQL是一种描述请求数据的查询语言,一种规范,通常用于前后端数据交互,GraphQL的出现解决了RESTful请求造成的资源浪费,
如我需要查询用户的班级Id:
再比如我需要查找用户班级Id,跟班级名称
概括
简单的说,GraphQL 是一种描述请求数据方法的语法,通常用于客户端从服务端加载数据。GraphQL 有以下三个主要特征:
- 它允许客户端指定具体所需的数据。
- 它让从多个数据源汇总取数据变得更简单。
- 它使用了类型系统来描述数据。
GraphQL 提出的解决方案
Facebook 提出了一个概念很简单的解决方案:不再使用多个“愚蠢”的节点,而是换成用一个“聪明”的节点来进行复杂的查询,将数据按照客户端的要求传回。
实际上,GraphQL 层处于客户端与一个或多个数据源之间,它接收客户端的请求然后根据你的设定取出需要的数据。
理论上,一个 GraphQL API 主要由三个部分组成:schema(类型),queries(查询) 以及 resolvers(解析器)。
GraphQL按需索取数据,避免浪费
![](https://i-blog.csdnimg.cn/blog_migrate/ec36c88156164d149a150b7ae41bdcac.png)
可以看到,一次请求,不仅查询到了hero数据,而且还查询到了friends数据。节省了网络请求次数。
GraphQL查询规范
GraphQL定义了一套规范,用来描述语法定义,具体参考:http://graphql.cn/learn/queries/
字段(Fields)
![](https://i-blog.csdnimg.cn/blog_migrate/4805e2902a226ac1f9861d3b2b26d250.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1df90aa31423f4b1680a75651b4abd0d.png)
别名(Aliases)
如果你眼睛够锐利,你可能已经发现,即便结果中的字段与查询中的字段能够匹配,但是因为他们并不包含参数,你就没法通过不同参数来查询相同字段J(SON语法,同级不能出现相同name的值)。这便是为何你需要别名 —— 这可以让你重命名结果中的字段为任意你想到的名字。
GraphQL的Schema 和类型规范
Schema 是用于定义数据结构的,比如说,User对象中有哪些属性,对象与对象之间是什么关系等。
schema { #定义查询
query: UserQuery
}
type UserQuery { #定义查询的类型
user(id:ID) : User #指定对象以及参数类型
}
type User { #定义对象
id:ID! # !表示该属性是非空项
name:String
age:Int
}
标量类型
我们知道这些字段没有任何次级字段 —— 因为让它们是查询的叶子节点。
GraphQL 自带一组默认标量类型:
Int
:有符号 32 位整数。Float
:有符号双精度浮点值。String
:UTF‐8 字符序列。Boolean
:true
或者false
。ID
:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。
type Character {
name: String!
appearsIn: [Episode]!
}
此处我们使用了一个 String
类型,并通过在类型名后面添加一个感叹号!
将其标注为非空。这表示我们的服务器对于这个字段,总是会返回一个非空值,如果它结果得到了一个空值,那么事实上将会触发一个 GraphQL 执行错误,以让客户端知道发生了错误。
非空类型修饰符也可以用于定义字段上的参数,如果这个参数上传递了一个空值(不管通过 GraphQL 字符串还是变量),那么会导致服务器返回一个验证错误。
解析器Resolvers
GraphQL 服务端并不知道要对一个即将到来的查询做什么处理,除非你使用 resolver 来告诉他
resolver
是决定schemas
中的field
该如何执行的函数。
![](https://i-blog.csdnimg.cn/blog_migrate/cc9d9b2629c03286f1bb2ef72cf33aec.png)
导入Maven依赖:
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>11.0</version>
</dependency>
<!--io操作工具包-->
``````<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
说明:graphql-java包并没有发布到maven中央仓库,需要配置第三方仓库才能使用。在setting.xml文件里进行配置:
<profile>
<id>bintray</id>
<repositories>
<repository>
<id>bintray</id>
<url>http://dl.bintray.com/andimarek/graphql-java</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>bintray</id>
<url>http://dl.bintray.com/andimarek/graphql-java</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
<activeProfiles>
<activeProfile>bintray</activeProfile>
</activeProfiles>
创建User对象
public class User {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
schema {
query: UserQuery
}
type UserQuery {
user(id:Long) : User
}
type User {
id:Long!
name:String
age:Int
}
构建schema
package cn.xiechuang.graphql.pojo;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
public class GraphQLDemo {
/***
* 定义Schema*
* <p>* schema { #定义查询* query: UserQuery* }** @return*/
public static GraphQLSchema createGraphqlSchema(TypeDefinitionRegistry
typeRegistry, RuntimeWiring wiring) {
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, wiring);
}
/***
* 读取文件内容
*
* @param fileName classpath:文件名称
*/
public static String readFile(String fileName) throws IOException {
return IOUtils.toString(GraphQLDemo.class.getClassLoader().getResourceAsStream(fileName), "utf-8");
}
/***
* 定义类型的注册器
*
* ** @param fileContent* @return
* */
public static TypeDefinitionRegistry createTypeDefinitionRegistry(String fileContent){
SchemaParser schemaParser = new SchemaParser();
return schemaParser.parse(fileContent);
}
public static RuntimeWiring createRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("UserQuery", typeWiring -> typeWiring
.dataFetcher("user", environment -> {
Long id = environment.getArgument("id");
return new User(id,"wiring"+id,15);
})
).build();
}
public static void main(String[] args) throws IOException {
// 读取Schema文件
String fileName = "user.graphqls";
String content = readFile(fileName);
// 创建注册器
TypeDefinitionRegistry typeDefinitionRegistry = createTypeDefinitionRegistry(content);
// 创建resolver
RuntimeWiring runtimeWiring = createRuntimeWiring();
// 载入Schema
GraphQL graphQL = GraphQL.newGraphQL(createGraphqlSchema(typeDefinitionRegistry, runtimeWiring)).build();
// 使用query查询
ExecutionResult execute = graphQL.execute("{user(id:1){id,name}}");
System.out.println((Object) execute.getData());
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/dc7d4c61f3fdf1efca0c7c041f922ca5.png)
建议:
api可能有点复杂,过一下流程就行,代码用的时候可以copy,下一篇章会讲如何整合springboot怎么引入到项目中,~^v^