GraphQL快速入门-JAVA

GraphQL

GraphQL是一种描述请求数据的查询语言,一种规范,通常用于前后端数据交互,GraphQL的出现解决了RESTful请求造成的资源浪费,

如我需要查询用户的班级Id:

# 请求 GET http://127.0.0.1/user/1001
#响应    {"id":10037,"name":"ls","classId":"10341"}
 
 
响应结果响应了多余的数据,id,name,这其实算是一种资源的浪费,尽管微不足道
 
 
 

再比如我需要查找用户班级Id,跟班级名称

# 请求 GET http://127.0.0.1/user/1001
#响应    {"id":10037,"name":"ls","classId":"10341"}
 
# 请求 GET http://127.0.0.1/class/10341
#响应    {"classId":10037,"name":"计算机一班",.....}

 

查询用户以及他的身份证信息,需要进行 2 次查询才能够完成,这样对于前端等接口的使用方是很不友好的,试想一下,如果查询信息有 10 个,是不是要发起 10 次请求才能完成?
 
 

概括

简单的说,GraphQL 是一种描述请求数据方法的语法,通常用于客户端从服务端加载数据。GraphQL 有以下三个主要特征:

  • 它允许客户端指定具体所需的数据。
  • 它让从多个数据源汇总取数据变得更简单。
  • 它使用了类型系统来描述数据。

 

GraphQL 提出的解决方案

Facebook 提出了一个概念很简单的解决方案:不再使用多个“愚蠢”的节点,而是换成用一个“聪明”的节点来进行复杂的查询,将数据按照客户端的要求传回。

实际上,GraphQL 层处于客户端与一个或多个数据源之间,它接收客户端的请求然后根据你的设定取出需要的数据。

理论上,一个 GraphQL API 主要由三个部分组成:schema(类型)queries(查询) 以及 resolvers(解析器)

 

GraphQL按需索取数据,避免浪费

GraphQL快速入门-JAVA_第1张图片

 

GraphQL快速入门-JAVA_第2张图片

 

可以看出请求中只有name属性,响应结果中也只包含name属性,如果请求中添加height,mass属性,那么结果也会根据请求返回
 
 
GraphQL一次请求多个数据
GraphQL快速入门-JAVA_第3张图片

可以看到,一次请求,不仅查询到了hero数据,而且还查询到了friends数据。节省了网络请求次数。

 

GraphQL查询规范

GraphQL定义了一套规范,用来描述语法定义,具体参考:http://graphql.cn/learn/queries/

字段(Fields

GraphQL 的查询中,请求结构中包含了所预期结果的结构,这个就是字段。并且响应的结构和请求结构基本一致,这是 GraphQL 的一个特性,这样就可以让请求发起者很清楚的知道自己想要什么。
GraphQL快速入门-JAVA_第4张图片

 

参数( Arguments
在查询数据时,离不开传递参数,在 GraphQL 的查询中,也是可以传递参数的,语法: ( 参数名 : 参数值 )
GraphQL快速入门-JAVA_第5张图片

别名(Aliases)

如果你眼睛够锐利,你可能已经发现,即便结果中的字段与查询中的字段能够匹配,但是因为他们并不包含参数,你就没法通过不同参数来查询相同字段J(SON语法,同级不能出现相同name的值)。这便是为何你需要别名 —— 这可以让你重命名结果中的字段为任意你想到的名字。

 

GraphQL快速入门-JAVA_第6张图片

 

GraphQLSchema 和类型规范

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 字符序列。
  • Booleantrue 或者 false
  • ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。
规范中定义的这 5 种类型,显然是不能满足需求的,所以在各种语言实现中,都有对类型进行了扩充,也就是GraphQL 支持自定义类型,比如在 graphql-java 实现中增加了: Long Byte 等。
 
列表和非空
对象类型、标量以及枚举是 GraphQL 中你唯一可以定义的类型种类。但是当你在 schema 的其他部分使用这些类型时,或者在你的查询变量声明处使用时,你可以给它们应用额外的 类型修饰符来影响这些值的验证。我们先来看一个例子:
type Character {
  name: String!
  appearsIn: [Episode]!
}

此处我们使用了一个 String 类型,并通过在类型名后面添加一个感叹号!将其标注为非空。这表示我们的服务器对于这个字段,总是会返回一个非空值,如果它结果得到了一个空值,那么事实上将会触发一个 GraphQL 执行错误,以让客户端知道发生了错误。

非空类型修饰符也可以用于定义字段上的参数,如果这个参数上传递了一个空值(不管通过 GraphQL 字符串还是变量),那么会导致服务器返回一个验证错误。

 

解析器Resolvers

GraphQL 服务端并不知道要对一个即将到来的查询做什么处理,除非你使用 resolver 来告诉他

resolver是决定schemas中的field该如何执行的函数。

 

GraphQL Java 实现
 
官网: https://www.graphql-java.com
 
GraphQL快速入门-JAVA_第7张图片

 导入Maven依赖:

        
            com.graphql-java
            graphql-java
            11.0
        


 ``````
            org.apache.commons
            commons-io
            1.3.2
        

说明:graphql-java包并没有发布到maven中央仓库,需要配置第三方仓库才能使用。setting.xml文件里进行配置:


		bintray
		
			
				bintray
				http://dl.bintray.com/andimarek/graphql-java
				
				true
				
				
				false
				
				
			
		
		
			bintray
			http://dl.bintray.com/andimarek/graphql-java
			
			true
			
			
			false
			
			
		
	


 
    bintray
  

 创建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
 
resources 目录下创建 user.graphqls 文件
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*
     * 

* 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()); } }

 

测试
GraphQL快速入门-JAVA_第8张图片

 

 

建议:

api可能有点复杂,过一下流程就行,代码用的时候可以copy,下一篇章会讲如何整合springboot怎么引入到项目中,~^v^

你可能感兴趣的:(web项目)