SpringBoot 中集成 GraphQL:入門篇

說實話,網上這玩意資料太少了,用的人也挺少的,不是因爲技術需求,估計大家都不會想用,這次來把這個小東西稍微記錄一下

一、關於 GrapQL
1、什麼是 GraphQL?
官方解釋:一種用於 API 的查詢語言
看了這句話之後我是懵逼的,沒看懂,如果你也沒看懂看看下面這句話

請求你所要的數據,不多不少
什麼意思呢,很簡單,假如我有一個查詢用戶信息的 restful 接口,裏面會返回一些信息,用戶名,密碼,頭像,等等,現在前端和我說,

前端:我只要用戶名和頭像,其他的都不要,你別查出來給我了
我:what?你不要的數據你不用不就行了,這接口不止你 web 端用,還有 app 接口也在用呢
前端:那我不管,現在需求就是這樣
我:……

這個時候 GraphQL 出現了,你想要用戶名和頭像是吧,行,你自己來取,你要什麼自己取什麼,後端就會給你返回什麼,後端也不用單獨來給你寫接口,說到這裏,你可能似懂非懂,沒關係,接着看下去

2、怎麼使用?
彆着急,繼續往下看

二、Idea 中安裝 GraphQL 插件
1、安裝 JS GraphQL 插件


2、安裝之後
新建文件可以選擇新建 graphql 文件


工具欄也會出現 graphql
這裏面會自動去檢查我們寫的 graphql 文件中語法是否正確


三、編寫一個簡單的 GrapQL
1、引入相關依賴
         <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
 <!-- 必需:包含了默認配置、graphql-java 和 graphql-java-tools,可以簡化配置 -->
        <dependency>
            <groupId>com.graphql-java-kickstart</groupId>
            <artifactId>graphql-spring-boot-starter</artifactId>
            <version>11.0.0</version>
        </dependency>

        <!-- 可選:用於調試 GraphQL,功能類似 Restful 中的 Swagger -->
        <dependency>
            <groupId>com.graphql-java-kickstart</groupId>
            <artifactId>playground-spring-boot-starter</artifactId>
            <version>11.0.0</version>
        </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2、yml 配置
# 端口
server:
  port: 7002

# 應用名稱
spring:
  application:
    name: graphql-demo

# graphql 相關配置
graphql:
  servlet:
    mapping: /graphql
    enabled: true
    corsEnabled: false # 關閉跨域,僅使用瀏覽器插件調試時設置爲false
    playground:
      cdn:
        enabled: true    # playground 使用 cdn 的靜態文件
    # if you want to @ExceptionHandler annotation for custom GraphQLErrors
    exception-handlers-enabled: true
    contextSetting: PER_REQUEST_WITH_INSTRUMENTATION
  tools:
    #掃描 resource 下 .graphql 後綴的文件
    schema-location-pattern: "**/*.graphql"


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
3、新建一個 clazz.graphql 文件
#定義查詢類型和更改類型
schema {
    query: Query
    mutation: Mutation
}

#查詢方法, 類型來源於上面定義的 Query
  type Query{
    #查詢所有班級,返回 list<Clazz>
    listClazz: [Clazz]
    #根據名稱查詢班級,返回單個 Clazz 對象
    listClazzByName(name:String!): Clazz
}

#變更方法 , 類型來源於上面定義的 Mutation
 type Mutation{
    # 新增班級(返回 Result 對象)
    add(code:String!,name:String!,description:String!): Result
    # 修改班級(返回 Result 對象)
    edit(id:String!,code:String!,name:String!,description:String!): Result
    # 刪除班級(返回 Result 對象)
    del(id:String!): Result
    # 創建班級(入參類型爲對象 ClazzInput)
    addByInput(input: ClazzInput!): Result
}

#班級實體
type Clazz{
    #id
    id : String
    #編號
    code : String!
    #名稱
    name : String!
    #描述
    description : String!
}


#班級 input 對象
input ClazzInput{
    #id
    id : String
    #編號
    code : String!
    #名稱
    name : String!
    #描述
    description : String!

}

type Result{
    #狀態碼
    code : Int!
    #狀態信息
    msg : String!

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
4、相關語法解釋
.graphql 文件中註釋用: #註釋內容

Query下定義的是查詢相關接口,Mutation下定義的是修改相關接口

Query 和 Mutation 方法格式
Query中方法格式爲 : 方法名:返回類型 例如:listClazz: [Clazz]

方法名(參數名:參數類型):返回類型 例如:listClazzByName(name:String!): Clazz 參數類型後面加 !
代表這個參數不能爲空,可爲空則不加 ! 即可

Mutation 中方法格式基本和 Query 一致

type Clazz 你可以理解爲定義一個名爲 Clazz 的 java 實體類,裏面就是它的一些屬性,type Result 也一樣

input ClazzInput 則是定義一個輸入類型,可以看到屬性基本和 Clazz 一致,只是定義時將 type 改成了 input,作用我們可以理解爲,我們可以直接傳輸這個對象,稍後來進行講解

5、編寫 java 實體類
先把兩個對應的 java 實體類寫好,屬性同 graphql 文件中對象一致

package com.wxw.notes.graphql.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author wuxiongwei
 * @date 2021/5/25 16:38
 * @Description
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Clazz {
    private String id;
    private String code;
    private String name;
    private String description;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.wxw.notes.graphql.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author wuxiongwei
 * @date 2021/5/25 16:38
 * @Description
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ClazzInput {
    private String id;
    private String code;
    private String name;
    private String description;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.wxw.notes.graphql.demo.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Result {
    private Integer code;
    private String msg;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
6、編寫 Query 和 Mutation 解析器
package com.wxw.notes.graphql.demo.resolver;

import com.wxw.notes.graphql.demo.entity.Clazz;
import com.wxw.notes.graphql.demo.entity.ClazzInput;
import com.wxw.notes.graphql.demo.response.Result;
import com.wxw.notes.graphql.demo.service.ClazzService;
import graphql.kickstart.tools.GraphQLMutationResolver;
import graphql.kickstart.tools.GraphQLQueryResolver;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author wuxiongwei
 * @date 2021/5/25 16:37
 * @Description
 */
@Component
public class ClazzResolver implements GraphQLQueryResolver, GraphQLMutationResolver {

    private final ClazzService clazzService;

    public ClazzResolver(ClazzService clazzService) {
        this.clazzService = clazzService;
    }

    public List<Clazz> listClazz(){
        return clazzService.list();
    }

    public Clazz listClazzByName(String name){
        return clazzService.listByName(name);
    }

    public Result add(String code, String name, String description){
        return clazzService.add(code,name,description);
    }

    public Result del(String id){
        return clazzService.del(id);
    }

    public Result edit(String id,String code, String name, String description){
        return clazzService.edit(id,code,name,description);
    }

    public Result addByInput(ClazzInput cla){
        return clazzService.addByInput(cla);
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
GraphQLQueryResolver 顧名思義就是 Query 查詢相關的解析器了
GraphQLMutationResolver 就是 Mutation 更改相關的解析器了

我這裏就直接寫在一個類裏面了(可以選擇分開寫兩個類去分別實現),同時實現
GraphQLQueryResolver 和 GraphQLMutationResolver

7、編寫 Service 層方法
在 service 層,我們一般是查詢數據庫數據,之前怎麼查現在還是怎麼查,在這裏我就簡單在內存裏面初始化一些數據了

package com.wxw.notes.graphql.demo.service;

import com.wxw.notes.graphql.demo.entity.Clazz;
import com.wxw.notes.graphql.demo.entity.ClazzInput;
import com.wxw.notes.graphql.demo.response.Result;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author wuxiongwei
 * @date 2021/5/25 16:38
 * @Description
 */
@Service
public class ClazzService {

    private List<Clazz> clazzList = new ArrayList<Clazz>(){{
        add(Clazz.builder().id(UUID.randomUUID().toString()).code("001").name("三年二班").description("加里頓第一班").build());
    }};


    public Clazz listByName(String name){
        Optional<Clazz> first = clazzList.stream().filter(clazz -> clazz.getName().equals(name)).findFirst();
        return first.orElse(null);
    }

    public List<Clazz> list(){
        return clazzList;
    }

    public Result add(String code, String name, String description){
        Clazz clazz = new Clazz();
        clazz.setId(UUID.randomUUID().toString());
        clazz.setCode(code);
        clazz.setName(name);
        clazz.setDescription(description);

        clazzList.add(clazz);

        return Result.builder().code(200).msg("success").build();
    }

    public Result del(String id){

        clazzList = clazzList.stream().filter(clazz -> !clazz.getId().equals(id)).collect(Collectors.toList());
        return Result.builder().code(200).msg("success").build();
    }

    public Result edit(String id,String code, String name, String description){
        del(id);
        add(code, name, description);
        return Result.builder().code(200).msg("success").build();
    }

    public Result addByInput(ClazzInput cla){
        Clazz clazz = new Clazz();
        clazz.setId(UUID.randomUUID().toString());
        clazz.setCode(cla.getCode());
        clazz.setName(cla.getName());
        clazz.setDescription(cla.getDescription());
        clazzList.add(clazz);
        return Result.builder().code(200).msg("success").build();
    }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
8、見證 GraphQL 使用
一切準備工作就緒之後,我們用 playground 圖形化工具來看看 graphql 到底怎麼來進行使用

啓動我們的工程,訪問 localhost:7002/playground
就可以看到下面這個界面了,默認界面風格是黑色,如果你想要修改成我這顏色,點擊右上角設置
“editor.theme” 的值修改成 “light” 然後點擊右上角保存即可

左邊的查詢語句,可能大家一進來就是空的,截圖的查詢語句是我這邊之前測試寫的

接下來,我們依次來調用下我們寫的 graphql 方法,看看怎麼調用
首先調用查詢所有班級的方法,可以點開右側 DOCS 文檔,對照來寫,我們想查詢哪些信息,我左邊就寫哪些字段,我先查詢所有字段

8.1、Query 查詢
# listClazz 調用的方法名
# id,code,name,description 調用後想要得到的返回值
{
  listClazz {
    id
    code
    name
    description
  }
}
1
2
3
4
5
6
7
8
9
10

這個時候我們來解決前端給我的問題,假如小夥子不要 id 了,簡單,你自己查詢的時候直接去掉就行了,這樣我就不給你 id 了,如下

# listClazz 調用的方法名
# code,name,description 調用後想要得到的返回值
{
  listClazz {
    code
    name
    description
  }
}
1
2
3
4
5
6
7
8
9

再調用根據名稱查詢班級的方法,直接放入參數

# listClazzByName 調用的方法名
# name 參數屬性名,三年二班 參數值
# code,name,description 調用後想要得到的返回值
  listClazzByName(name:"三年二班"){
    code
    name
    description
  }
1
2
3
4
5
6
7
8


8.2 、使用變量 Variables
再調用根據名稱查詢班級的方法,使用變量 Variables 傳入參數

# query  查詢關鍵字
# listClazzByName 別名
# $name:String! name 爲聲明變量,String 類型,不爲空(變量前加 $)
# listClazzByName 方法名,name 屬性名,$name 引用上面聲明的變量
# code,name,description 查詢後想要的返回值
query listClazzByName($name:String!){
  listClazzByName(name:$name){
    code
    name
    description
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
# json 格式
{
  "name":"三年二班"
}
1
2
3
4


8.3、 Mutation 變更
我們來單獨寫變更中的 新增

 # mutation 變更關鍵字
 # add 方法名
 # code,name,description 參數
 # code,msg 請求後想要返回的返回值
  mutation{
    add(code: "001"
      name: "三年三班"
        description: "加里頓第二班級"){
      code
      msg
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12


新增之後,再查詢所有看看,可以看到已經成功添加了

8.4、Input 輸入類型
接下來我們看看上文說到的輸入類型怎麼使用

# mutation 變更關鍵字
# addByInput 別名
# $clazzInput:ClazzInput! ,聲明變量 $clazzInput ,類型爲 ClazzInput ,不爲空
# addByInput 接口名,input:$clazzInput  input 入參類型, $clazzInput 引用變量
# code,msg 調用接口後想要的返回值

mutation addByInput($clazzInput:ClazzInput!){
  addByInput(input:$clazzInput){
    code
    msg
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
# 參數
{
  "clazzInput": {
    "code": "003",
    "name": "三年四班",
    "description": "加里頓第三班"
  }
}
1
2
3
4
5
6
7
8

新增之後,再查詢所有看看,可以看到已經成功添加了


好了,基本用法就分享到這裏了,大家有興趣可以自行查閱官網

官網地址

四、總結
1、這只是一個簡單的 demo,我們 java 中複雜的實體對象怎麼實現呢?嵌套對象,嵌套集合等等
2、多個 .graphql 文件怎麼引用呢?我們不可能一個 graphql 文件裏面把所有的實體類都寫了
3、使用 graphql 我們會遇到哪些問題?怎麼去解決定位呢?
是不是覺得不過癮,感覺還沒有看到你想要的,想了解更多的可以進入下一篇深入瞭解,源碼地址也會在下一篇進行分享
————————————————

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章