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

    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM