Spring Boot GraphQL 實戰 02_增刪改查和自定義標量

hello,大叫好,我是小黑,又和大家見面啦~

今天我們來繼續學習 Spring Boot GraphQL 實戰,我們使用的框架是 https://github.com/graphql-java-kickstart/graphql-spring-boot

項目 github 地址:https://github.com/shenjianeng/graphql-spring-boot-example

Query(查詢)

帶參數的查詢

首先,在 classpath 下創建 graphqls 文件:

type Book{
    id:ID!
    name:String!
}

type Query{
    # 根據 id 查詢 book,參數名為 id,參數類型的 ID 類型,結果返回 book
    getBookById(id:ID!):Book
}

創建一個 Spring Bean,此處需要實現 GraphQLQueryResolver 接口,并在該類中自定義一個方法來映射 graphqls 文件中的查詢。

@Data
public class Book {
    private int id;
    private String name;
}

@Component
public class BookGraphQLQueryResolver implements GraphQLQueryResolver {

    public Book getBookById(int id) {
        Book book = new Book();
        book.setId(id);
        book.setName("這邊書沒有書名");
        return book;
    }
}

復合字段查詢

需求:每本書都有作者,在查詢書本信息時,有時需要返回作者信息。

# 定義 Author 數據類型結構
type Author{
    id:ID!
    name:String!
}

type Book{
    id:ID!
    name:String!
    # 增加 author 字段,數據類型為 Author
    author:Author
}


type Query{
    # 根據 id 查詢 book,參數名為 id,參數類型的 ID 類型,結果返回 book
    getBookById(id:ID!):Book
}

再看一下此時我們的 Java Bean:

@Data
public class Author {
    private UUID id;
    private String name;
}

@Data
public class Book {
    private long id;
    private String name;
}

看仔細哦,Book 類中并沒有 author 字段,Book 中 author 信息將由 graphql.kickstart.tools.GraphQLResolver 來提供。

@Slf4j
@Component
public class BookGraphQLResolver implements GraphQLResolver<Book> {

    public Author author(Book book) {
        log.info("book id :{} query author info", book.getId());
        Author author = new Author();
        author.setId(UUID.randomUUID());
        author.setName(String.format("我是[%s]的作者", book.getName()));
        return author;
    }
}

ok,讓我們啟動服務,訪問 http://localhost:8080/graphiql

同時查詢book和author

而當客戶端不需要 author 信息時,服務端就不會執行 BookGraphQLResolver#author,真正做到了使得客戶端能夠準確地獲得它需要的數據,而且沒有任何冗余

(ps:如果你是服務端開發,你會怎么實現呢?是給客戶端提供一個接口返回 book 和 author 信息,還是給客戶端提供兩個不同的接口呢?)

只查詢book時

Mutation(變更)

在 graphqls 文件中,使用 Query 來定義查詢接口,使用 Mutation 可以定義變更數據的操作。

type Mutation{
    createBook(id:ID!,name:String!):Book
}

上述 graphqls 文件中定義了一個 createBook 的方法,參數列表為 idname ,方法返回創建的 Book 對象。

與之對應的 Java 代碼如下:

@Component
public class BookGraphQLMutationResolver implements GraphQLMutationResolver {

    public Book createBook(int id, String name) {
        Book book = new Book();
        book.setId(id);
        book.setName(name);
        return book;
    }
}

BookGraphQLMutationResolver 實現了 graphql.kickstart.tools.GraphQLMutationResolver 接口,表明當前類中的方法用來映射 graphqls 文件中的 Mutation。

mutation

Input Types

當 Mutation 中請求參數特別多時,我們可以使用 Input Types 來優化代碼。

type Mutation{
    createBook(id:ID!,name:String!):Book
    create(bookInput:BookInput!):Book
}

input BookInput{
    id:ID!
    name:String!
}

同理,我們也需求在 BookGraphQLMutationResolver 中添加對應的方法來映射。

@Component
public class BookGraphQLMutationResolver implements GraphQLMutationResolver {
    // ...省略其他代碼
  
    public Book create(BookInput input) {
        Book book = new Book();
        book.setId(input.getId());
        book.setName(input.getName());
        return book;
    }
}

客戶端請求代碼如下:

mutation和input

自定義標量類型

在 GraphQL 中自帶一些默認標量類型:

  • Int:有符號 32 位整數

  • Float:有符號雙精度浮點值

  • String:UTF‐8 字符序列

  • Booleantrue 或者 false

  • ID:ID 標量類型表示一個唯一標識符,通常用以重新獲取對象或者作為緩存中的鍵。ID 類型使用和 String 一樣的方式序列化

使用 graphql-java-extended-scalars 庫

在 Java 這個生態中,我們可以引入下面這個庫來幫助我們很方便的進行擴展:

https://github.com/graphql-java/graphql-java-extended-scalars

  <dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-extended-scalars</artifactId>
    <version>15.0.0</version>
  </dependency>

graphql-java-extended-scalars 中具體擴展了哪些標量類型,我們都可以在 graphql.scalars.ExtendedScalars 類中找到。

(ps:一個小技巧,s 結尾的類一般都是工具類)

ExtendedScalars

如何使用呢?

  1. 向 Spring 容器中注冊自定義標量
  2. 在 graphqls 文件中聲明要使用的自定義標量
  3. 直接使用即可

相關示例代碼如下:

@Configuration
public class CustomScalarTypeConfig {

    @Bean
    public GraphQLScalarType graphQLLong() {
        return ExtendedScalars.GraphQLLong;
    }
}
scalar Long

type Book{
    id:ID!
    name:String!
    # 增加 author 字段,數據類型為 Author
    author:Author
    totalPageSize:Long
}

使用 GraphQLScalarType 自定義標量類型

我們可以參考 graphql.scalars.java.JavaPrimitives#GraphQLLong 的實現來自定標量類型。

@Bean
public GraphQLScalarType graphQLDate() {
    return GraphQLScalarType
            .newScalar()
            .name("Date")
            .description("Date 類型")
            .coercing(new Coercing<Date, String>() {
                @Override
                public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
                    return new SimpleDateFormat(DATE_FORMAT_PATTERN_DEFAULT).format((Date) dataFetcherResult);
                }

                @Override
                public Date parseValue(Object input) throws CoercingParseValueException {
                    if (input instanceof String) {
                        try {
                            return new SimpleDateFormat(DATE_FORMAT_PATTERN_DEFAULT).parse((String) input);
                        } catch (ParseException e) {
                            throw new CoercingParseValueException(e);
                        }
                    }
                    throw new CoercingParseValueException(
                            "Expected a 'String' but was '" + Kit.typeName(input) + "'."
                    );
                }

                @Override
                public Date parseLiteral(Object input) throws CoercingParseLiteralException {
                    if (!(input instanceof StringValue)) {
                        throw new CoercingParseLiteralException(
                                "Expected AST type 'StringValue' but was '" + typeName(input) + "'."
                        );
                    }
                    try {
                        return new SimpleDateFormat(DATE_FORMAT_PATTERN_DEFAULT).parse(((StringValue) input).getValue());
                    } catch (ParseException e) {
                        throw new CoercingParseValueException(e);
                    }
                }
            })
            .build();
}

DataFetcherResult

在 Resolver 中,我們可以使用 graphql.execution.DataFetcherResult 來包裝返回的結果,示例代碼如下:

@Component
public class BookGraphQLQueryResolver implements GraphQLQueryResolver {

    public DataFetcherResult<Book> getBookById(int id) {
        if (id <= 0) {
            return DataFetcherResult
                    .<Book>newResult()
                    .error(new GenericGraphQLError("id 不能為負數"))
                    .build();
        }

        Book book = new Book();
        book.setId(id);
        book.setName("這邊書沒有書名");
        return DataFetcherResult
                .<Book>newResult()
                .data(book)
                .build();
    }
}

下期預告

下期我們將使用 graphQL 來實現分頁,并介紹一些高級特性,例如:異步加載、全局異常處理等。感謝大家的關注和閱讀~~

更多學習參考資料:

https://www.graphql-java-kickstart.com/tools/schema-definition/#resolvers-and-data-classes

https://graphql.org/learn/schema/

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容