GraphQL
GraphQL是一種描述請求數據的查詢語言,一種規範,通常用於前後端數據交互,GraphQL的出現解決了RESTful請求造成的資源浪費,
如我需要查詢用戶的班級Id:
再比如我需要查找用戶班級Id,跟班級名稱
概括
簡單的說,GraphQL 是一種描述請求數據方法的語法,通常用於客戶端從服務端加載數據。GraphQL 有以下三個主要特徵:
- 它允許客戶端指定具體所需的數據。
- 它讓從多個數據源彙總取數據變得更簡單。
- 它使用了類型系統來描述數據。
GraphQL 提出的解決方案
Facebook 提出了一個概念很簡單的解決方案:不再使用多個“愚蠢”的節點,而是換成用一個“聰明”的節點來進行復雜的查詢,將數據按照客戶端的要求傳回。
實際上,GraphQL 層處於客戶端與一個或多個數據源之間,它接收客戶端的請求然後根據你的設定取出需要的數據。
理論上,一個 GraphQL API 主要由三個部分組成:schema(類型),queries(查詢) 以及 resolvers(解析器)。
GraphQL按需索取數據,避免浪費
可以看到,一次請求,不僅查詢到了hero數據,而且還查詢到了friends數據。節省了網絡請求次數。
GraphQL查詢規範
GraphQL定義了一套規範,用來描述語法定義,具體參考:http://graphql.cn/learn/queries/
字段(Fields)
別名(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
該如何執行的函數。
導入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());
}
}
建議:
api可能有點複雜,過一下流程就行,代碼用的時候可以copy,下一篇章會講如何整合springboot怎麼引入到項目中,~^v^