GraphQL的基本使用

GraphQL定义

graphql是由Facebook创造的用于描述复杂数据模型的一种查询语言。这里查询语言所指的并不是常规意义上的类似sql语句的查询语言,而是一种用于前后端数据查询方式的规范。
官网中文:https://graphql.cn/
规范地址::http://spec.graphql.cn/
在这里插入图片描述

RESTful问题

RESTful是我们已经很熟悉的用于api通信的规范,如这样:

GET http://127.0.0.1/user/1 #查询 POST 
http://127.0.0.1/user #新增 PUT 
http://127.0.0.1/user #更新 DELETE 
http://127.0.0.1/user #删除

在查询的时候,往往是这样:

#请求 GET http://127.0.0.1/user/1001 
#响应:
 { 
     id : 1001,
     name : "张三", 
     age : 20, 
     address : "北京市",
     …… 
   }

这样似乎是没有问题的,如果,对于这次请求,我只需要id和name属性,其他的属性我都不需要,如果我依然拿
到的是全部的属性,这是不是一种资源浪费?
还有这样的一种场景,就是一次请求不能满足需求,需要有多次请求才能完成,像这样:

#查询用户信息 
GET http://127.0.0.1/user/1001 
#响应: 
{ 
      id : 1001, name : "张三", 
      age : 20, 
      address : "北京市",
      …… 
 }
 #查询用户的身份证信息 
 GET http://127.0.0.1/card/8888
  #响应: 
  { 
     id : 8888,
     name : "张三",
     cardNumber : "999999999999999", address :"北京市",
     …… 
     }

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

了解GraphQL

GraphQL很好的解决了RESTful在使用过程中的不足,接下来,我们进一步了解下它。

按需索取数据

演示地址:http://graphql.cn/learn/schema/#type-system
在这里插入图片描述
在这里插入图片描述
可以看出,当请求中只有name属性时,响应结果中只包含name属性,如果请求中添加appearsIn属性,那么结果
中就会返回appearsIn的值。

查询多个数据

在这里插入图片描述
可以看到,一次请求,不仅查询到了hero数据,而且还查询到了friends数据。节省了网络请求次数。

API的演进无需划分版本

查看官网的动画演示:http://graphql.cn/
在这里插入图片描述
在这里插入图片描述
从这些图中可以看出,当API进行升级时,客户端可以不进行升级,可以等到后期一起升级,这样就大大减少了客
户端和服务端的耦合度。

GraphQL查询的规范

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

说明:仅仅是规范,并不是具体实现,需要各种语言进行实现。

下面我们会讲一些基本的规范,更多的规范查看:http://graphql.cn/learn/queries/

字段(Fields)

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

参数(Arguments)

在查询数据时,离不开传递参数,在GraphQL的查询中,也是可以传递参数的,语法:(参数名:参数值)
在这里插入图片描述

别名(Aliases)

如果一次查询多个相同对象,但是值不同,这个时候就需要起别名了,否则json的语法就不能通过了。
比如这样:
在这里插入图片描述

片段(Fragments)

查询对的属相如果相同,可以采用片段的方式进行简化定义。
在这里插入图片描述

GraphQL的Schema 和类型规范

Schema 是用于定义数据结构的,比如说,User对象中有哪些属性,对象与对象之间是什么关系等
参考官网文档:http://graphql.cn/learn/schema/

Schema定义结构

schema { #定义查询 
query: UserQuery
 }
type UserQuery { #定义查询的类型 
user(id:ID) : User #指定对象以及参数类型 }

type User { #定义对象 
id:ID! # !表示该属性是非空项
 name:String age:Int 
 }

标量类型(Scalar Types)

GraphQL规范中,默认定义了5种类型:

  • Int :有符号 32 位整数。
  • Float :有符号双精度浮点值。
  • String :UTF‐8 字符序列。
  • Boolean : true 或者 false 。
  • ID :ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。

规范中定义的这5种类型,显然是不能满足需求的,所以在各种语言实现中,都有对类型进行了扩充,也就是
GraphQL支持自定义类型,比如在graphql-java实现中增加了:Long、Byte等。

枚举类型

枚举类型是一种特殊的标量,它限制在一个特殊的可选值集合内。

enum Episode { #定义枚举
 NEWHOPE
  EMPIRE 
  JEDI 
  }
  type Human { 
  id: ID! 
  name: String! 
  appearsIn: [Episode]! #使用枚举类型 
  homePlanet: String 
  } 

接口(interface)

跟许多类型系统一样,GraphQL 支持接口。一个接口是一个抽象类型,它包含某些字段,而对象类型必须包含这
些字段,才能算实现了这个接口。
例如,你可以用一个 Character 接口用以表示《星球大战》三部曲中的任何角色:

interface Character { #定义接口 
   id: ID! 
   name: String!
   friends: [Character] 
   appearsIn: [Episode]! 
   }
 #实现接口
 type Human implements Character { 
   id: ID! 
   name: String!
   friends: [Character]
   appearsIn: [Episode]! 
   starships: [Starship]
    totalCredits: Int 
 }
type Droid implements Character {
    id: ID!
    name: String! 
    friends:  [Character] 
    appearsIn: [Episode]! 
    primaryFunction: String
 }

GraphQL的Java实现

通过前面的讲解,我们对GraphQL有了解,官方只是定义了规范并没有做实现,就需要有第三方来进行实现了,关
于GraphQL的java实现有几种,我们选择使用官方推荐的实现:graphql-java,我们通过该实现就可以编写
GraphQL的服务端了。
官网:https://www.graphql-java.com/
github:https://github.com/graphql-java/graphql-java
在这里插入图片描述
在maven中先添加graphql的配置先。

<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>

  </profiles>

  <!-- activeProfiles
   | List of profiles that are active for all builds.
   |
  <activeProfiles>
    <activeProfile>alwaysActiveProfile</activeProfile>
    <activeProfile>anotherAlwaysActiveProfile</activeProfile>
  </activeProfiles>
  -->
  <activeProfiles>
    <activeProfile>bintray</activeProfile>
  </activeProfiles>

1. 导入依赖

 <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java</artifactId>
            <version>11.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>

2. 编写实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private Card card;
private String cardNumber;
    private Long userId;

    public Card() {
    }

    public Card(String cardNumber, Long userId) {
        this.cardNumber = cardNumber;
        this.userId = userId;
    }

    public String getCardNumber() {
        return cardNumber;
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

3. dome的编写


    public static void main(String[] args) {

        /**
         * type User { #定义对象}
         */
        GraphQLObjectType userObjectType = newObject()
                .name("User")
                .field(newFieldDefinition().name("id").type(GraphQLLong))
                .field(newFieldDefinition().name("name").type(GraphQLString))
                .field(newFieldDefinition().name("age").type(GraphQLInt))
                .build();

        /**
         * user : User #指定对象以及参数类型
         */
        GraphQLFieldDefinition userFieldDefinition = newFieldDefinition()
                .name("user")
                .type(userObjectType)
                .argument(newArgument().name("id").type(GraphQLLong).build())
                .dataFetcher(environment -> {
                    Long id = environment.getArgument("id");
                    // 查询数据库了
                    // TODO 先模式实现
                    return  new User(id, "张三:"+id, 20+id.intValue(),null);
                })
//                .dataFetcher(new StaticDataFetcher(new User(1L, "张三", 20)))
                .build();

        /**
         * type UserQuery { #定义查询的类型}
         */
        GraphQLObjectType userQueryObjectType = newObject()
                .name("UserQuery")
                .field(userFieldDefinition)
                .build();

        /**
         * schema { #定义查询 }
         */
        GraphQLSchema graphQLSchema = GraphQLSchema.newSchema().query(userQueryObjectType).build();

        GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();

        String query = "{user(id:100){id,name}}";
        ExecutionResult result = graphQL.execute(query);

        System.out.println("query:" + query);
//        System.out.println(result.getErrors());
//        System.out.println(result.getData());

        System.out.println(result.toSpecification());

    }

第二种方案的编写

在第一种方案的实体类和依赖基础上。
1. 编写文件

schema {
    query: UserQuery
}
type UserQuery {
    user(id:Long) : User
}
type User {
    id:Long!
    name:String
    age:Int
    card:Card
}

type Card{
    cardNumber:String
    userId:Long
}

==2. 代码编写 ==

package com.sj.vo.demo;


import com.sj.vo.Card;
import com.sj.vo.User;
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 GraphQLSDLDemo {

    public static void main(String[] args) throws IOException {

        // 读取GraphQL文件,进行解析
        String fileName = "user.graphql";
        String fileContent = IOUtils.toString(GraphQLSDLDemo.class.getClassLoader().getResource(fileName), "UTF-8");
        TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(fileContent);

        // 解决的是数据的查询
        RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring()
                .type("UserQuery", builder ->
                        builder.dataFetcher("user", environment -> {
                            Long id = environment.getArgument("id");
                            Card card = new Card("123456789", id);
                            return new User(id, "张三:" + id, 20 + id.intValue(), card);
                        })
                )
                .build();

        // 生成Schema
        GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);

        // 根据Schema对象生成GraphQL对象
        GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();

        String query = "{user(id:100){id,name,age,card{cardNumber}}}";
        ExecutionResult result = graphQL.execute(query);

        System.out.println("query:" + query);
        System.out.println(result.toSpecification());

    }
}

posted @ 2020-08-09 11:45  杰的博客#  阅读(1707)  评论(0编辑  收藏  举报