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按需索取数据,避免浪费

 

 

可以看出请求中只有name属性,响应结果中也只包含name属性,如果请求中添加height,mass属性,那么结果也会根据请求返回
 
 
GraphQL一次请求多个数据

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

 

GraphQL查询规范

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

字段(Fields

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

 

参数( Arguments
在查询数据时,离不开传递参数,在 GraphQL 的查询中,也是可以传递参数的,语法: ( 参数名 : 参数值 )

别名(Aliases)

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

 

 

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
 

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

    }

}

 

测试

 

 

建议:

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
graphql-java 是 GraphQLJava 实现。这个库的目标是用于真实的生产环境。graphql-java 解析和执行查询 GraphQL 。它并不真正获取任何数据的:数据来源于执行回调或提供静态数据graphql-java 的 "hello world":import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import static graphql.Scalars.GraphQLString; import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition; import static graphql.schema.GraphQLObjectType.newObject; public class HelloWorld {     public static void main(String[] args) {         GraphQLObjectType queryType = newObject()                         .name("helloWorldQuery")                         .field(newFieldDefinition()                                 .type(GraphQLString)                                 .name("hello")                                 .staticValue("world")                                 .build())                         .build();         GraphQLSchema schema = GraphQLSchema.newSchema()                         .query(queryType)                         .build();         Map result = new GraphQL(schema).execute("{hello}").getData();         System.out.println(result);         // Prints: {hello=world}     } } 标签:graphql
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值