GraphQL Java 基于SpringBoot实践

894 阅读3分钟

GraphQL 介绍

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

基于node的服务端开发中,GraphQL技术较为成熟常用,在基于Java的服务端开发中,由于国内对该API标准的了解程度不高,以及引入GraphQL可能需要维护两份重复数据(schema和相应java代码实现)。

开始

本文旨在从Java服务端开发的角度,介绍GraphQL的落地实践。根据官网的示例:GraphQL Java和Spring Boot入门 ,遗憾是使用Gradle构建。本文使用Maven方式构建SpringBoot的一般方式。根据官网的示例,基本能了解一些执行方式。

使用依赖为本文发布时间最新版本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 实例

官网推荐SDL的写法,也可以使用Java语句编写并解析,如果你喜欢强类型。?*.graphql的文件放置在src/main/resources目录下,通过读取文件并对其进行解析。通过引用声明Bean实例 GraphQL 到全局,就完成数据查询构建。

总体上,创建GraphQL和GraphQLSchema实例的过程如下所示:

实例GraphQL架构说明

RuntimeWiring 内编写执行函数数据返回DataFetcher类型,进行解析选择字段的函数。

    /**
     * 输出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();  // 数据方法对应编写
        SchemaGenerator schemaGenerator = new SchemaGenerator();  // 查询器构建
        // 查询生成
        GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
        return GraphQL.newGraphQL(graphQLSchema).build();
    }

    /**
     * 数据类型方法对应编写
     *
     * @return RuntimeWiring对应执行方法
     */
    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())
                )
                // 级联字段关联查询
                .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();
    }

执行方法

在controller中编写URL入口/graphql,进行支持getpost两种请求方式,将得到的参数数据进行处理后,使用graphQL执行查询器。可能很多人想处理错误和json输出的,请阅读官方对执行的更多操作方法。

    /**
     * 执行graphQL查询
     *
     * @param query         查询语句-类json字符
     * @param operationName 查询操作名称-默认空字符
     * @param variables     查询参数变量-map对象、默认为空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();
    }

最后

示例项目使用springbootmaven方式构建,微服务采用restfulgraphql两种方式进行开发,两者相辅相成,比如:上传、websocket等一些接口混用的模式。 示例代码:GitHub

springboot构建版本:2.2.2.RELEASE,建议改为你目前使用的版本,避免再次下载。

项目目录

题外学习