GraphQL Java 基于SpringBoot實(shí)踐

奚視界 -【紅昭愿】

GraphQL 介紹

GraphQL 既是一種用于 API 的查詢語言也是一個滿足你數(shù)據(jù)查詢的運(yùn)行時誉尖。 GraphQL 對你的 API 中的數(shù)據(jù)提供了一套易于理解的完整描述狞山,使得客戶端能夠準(zhǔn)確地獲得它需要的數(shù)據(jù)壶辜,而且沒有任何冗余,也讓 API 更容易地隨著時間推移而演進(jìn),還能用于構(gòu)建強(qiáng)大的開發(fā)者工具。

基于node的服務(wù)端開發(fā)中眼姐,GraphQL技術(shù)較為成熟常用,在基于Java的服務(wù)端開發(fā)中佩番,由于國內(nèi)對該API標(biāo)準(zhǔn)的了解程度不高众旗,以及引入GraphQL可能需要維護(hù)兩份重復(fù)數(shù)據(jù)(schema和相應(yīng)java代碼實(shí)現(xiàn))。

開始

本文旨在從Java服務(wù)端開發(fā)的角度趟畏,介紹GraphQL的落地實(shí)踐贡歧。根據(jù)官網(wǎng)的示例:GraphQL Java和Spring Boot入門 ,遺憾是使用Gradle構(gòu)建赋秀。本文使用Maven方式構(gòu)建SpringBoot的一般方式利朵。根據(jù)官網(wǎng)的示例,基本能了解一些執(zhí)行方式猎莲。

使用依賴為本文發(fā)布時間最新版本v14,另外使用mvcurl地址映射/graphql作為請求入口绍弟。

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java</artifactId>
            <version>14.0</version>
        </dependency>
    </dependencies>

GraphQL 實(shí)例

官網(wǎng)推薦SDL的寫法,也可以使用Java語句編寫著洼,如果你喜歡樟遣。?*.graphql的文件放置在src/main/resources目錄下,通過讀取文件并對其進(jìn)行解析身笤。通過引用聲明Bean實(shí)例 GraphQL 到全局年碘,就完成數(shù)據(jù)查詢構(gòu)建。

總體上展鸡,創(chuàng)建GraphQL和GraphQLSchema實(shí)例的過程如下所示:


實(shí)例GraphQL架構(gòu)說明

RuntimeWiring 內(nèi)編寫執(zhí)行函數(shù)數(shù)據(jù)返回DataFetcher類型屿衅,進(jìn)行解析選擇字段的函數(shù)。

    /**
     * 輸出world
     * qurey { hello }
     * @return 返回字符串
     */
    public DataFetcher getHelloWorldDataFetcher() {
        return dataFetchingEnvironment -> "world";
    }

主要編寫代碼如下:

    @Bean
    public GraphQL graphQL() throws IOException {
        // SDL讀取查詢類型文件莹弊,new SchemaParser().parse(?)解析File涤久、InputStream、String
        // ClassPathResource classPathResource = new ClassPathResource("schema.graphql");
        // TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(classPathResource.getInputStream());
        // 多SDL文件注冊
        // ClassPathResource UserSchema = new ClassPathResource("schema/UserSchema.graphql");
        // ClassPathResource schema = new ClassPathResource("schema/QuerySchema.graphql");
        // TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
        // SchemaParser schemaParser = new SchemaParser();
        // typeRegistry.merge(schemaParser.parse(UserSchema.getInputStream()));
        // typeRegistry.merge(schemaParser.parse(schema.getInputStream()));
        // 遍歷解析目錄下的schema忍弛,沒找到直接獲取文件列表的方法
        TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
        SchemaParser schemaParser = new SchemaParser();
        String[] schemaArr = {"UserSchema", "QuerySchema", "MutationSchema"};
        for (String str : schemaArr) {
            typeRegistry.merge(schemaParser.parse(new ClassPathResource("schema/" + str + ".graphql").getInputStream()));
        }
        
        RuntimeWiring runtimeWiring = buildWiring();  // 數(shù)據(jù)方法對應(yīng)編寫
        SchemaGenerator schemaGenerator = new SchemaGenerator();  // 查詢器構(gòu)建
        // 查詢生成
        GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
        return GraphQL.newGraphQL(graphQLSchema).build();
    }

    /**
     * 數(shù)據(jù)類型方法對應(yīng)編寫
     *
     * @return RuntimeWiring對應(yīng)執(zhí)行方法
     */
    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                // 查詢方法R/get
                .type("Query", builderFunction -> builderFunction
                        .dataFetcher("hello", graphQLDataFetchers.getHelloWorldDataFetcher())
                        .dataFetcher("echo", graphQLDataFetchers.getEchoDataFetcher())
                        .dataFetcher("users", userDataFetcher.getUsersDataFetcher())
                        .dataFetcher("user", userDataFetcher.getUserByIdDataFetcher())
                )
                // 級聯(lián)字段關(guān)聯(lián)查詢
                .type("User", builderFunction -> builderFunction.dataFetcher("info", userDataFetcher.getInfoDataFetcher()))
                // 增改刪方法CUD/post/put/del
                .type("Mutation", builderFunction -> builderFunction
                        .dataFetcher("createUser", userDataFetcher.createUserDataFetcher())
                        .dataFetcher("updateUser", userDataFetcher.updateUserDataFetcher())
                        .dataFetcher("deleteUser", userDataFetcher.deleteUserDataFetcher())
                )
                .build();
    }

執(zhí)行方法

在controller中編寫URL入口/graphql,進(jìn)行支持get响迂、post兩種請求方式,將得到的參數(shù)數(shù)據(jù)進(jìn)行處理后细疚,使用graphQL執(zhí)行查詢器蔗彤。可能很多人想處理錯誤和json輸出的,請閱讀官方對執(zhí)行的更多操作方法然遏。

    /**
     * 執(zhí)行g(shù)raphQL查詢
     *
     * @param query         查詢語句-類json字符
     * @param operationName 查詢操作名稱-默認(rèn)空字符
     * @param variables     查詢參數(shù)變量-map對象贫途、默認(rèn)為空map
     * @return map對象
     */
    private Map<String, Object> executeGraphqlQuery(String query, String operationName, Map<String, Object> variables) {
        ExecutionInput executionInput = ExecutionInput.newExecutionInput()
                .query(query)
                .operationName(operationName)
                .variables(variables)
                .build();
        return graphql.execute(executionInput).toSpecification();
    }

最后

示例項(xiàng)目使用springbootmaven方式構(gòu)建,微服務(wù)采用restfulgraphql兩種方式進(jìn)行開發(fā)待侵,兩者相輔相成丢早,比如:上傳、websocket等一些接口混用的模式秧倾。
示例代碼:GitHub

springboot構(gòu)建版本:2.2.2.RELEASE怨酝,建議改為你目前使用的版本,避免再次下載那先。

項(xiàng)目目錄

題外學(xué)習(xí)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末农猬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子售淡,更是在濱河造成了極大的恐慌盛险,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勋又,死亡現(xiàn)場離奇詭異,居然都是意外死亡换帜,警方通過查閱死者的電腦和手機(jī)楔壤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惯驼,“玉大人蹲嚣,你說我怎么就攤上這事∷钌” “怎么了隙畜?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長说贝。 經(jīng)常有香客問我议惰,道長,這世上最難降的妖魔是什么乡恕? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任言询,我火速辦了婚禮,結(jié)果婚禮上傲宜,老公的妹妹穿的比我還像新娘运杭。我一直安慰自己,他們只是感情好函卒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布辆憔。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虱咧。 梳的紋絲不亂的頭發(fā)上熊榛,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音彤钟,去河邊找鬼来候。 笑死,一個胖子當(dāng)著我的面吹牛逸雹,可吹牛的內(nèi)容都是我干的营搅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼梆砸,長吁一口氣:“原來是場噩夢啊……” “哼转质!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起帖世,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤休蟹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后日矫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赂弓,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年哪轿,在試婚紗的時候發(fā)現(xiàn)自己被綠了盈魁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡窃诉,死狀恐怖杨耙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情飘痛,我是刑警寧澤珊膜,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站宣脉,受9級特大地震影響车柠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜塑猖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一堪遂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧萌庆,春花似錦溶褪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吹菱。三九已至,卻和暖如春彭则,著一層夾襖步出監(jiān)牢的瞬間鳍刷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工俯抖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留输瓜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓芬萍,卻偏偏與公主長得像尤揣,于是被迫代替她去往敵國和親柬祠。 傳聞我的和親對象是個殘疾皇子北戏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容