GraphQL实战-第二篇-java实现及分析

公冶和豫
2023-12-01

GraphQL实战-第二篇-java实现及分析

https://blog.csdn.net/xplan5/article/details/108748841

到这里必须具备的知识储备:对GraphQL有简单的了解,了解Schema的常用类型。

这里用一些demo示例来体验GraphQL的执行过程,这只是借助graphql-java实现的java版本。

首先需要引入graphql-java的依赖

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

Demo1

第一个demo是官网提供的

import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.StaticDataFetcher;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;

import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring;

public class HelloWorld {

    public static void main(String[] args) {
        //定义schema文件,直接写在了代码中,包含一个hello的查询方法
        String schema = "type Query{hello: String} schema{query: Query}";
        SchemaParser schemaParser = new SchemaParser();
        //直接加载schema,初始化GraphQL
        TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema);
        //加载一份服务端数据
        RuntimeWiring runtimeWiring = new RuntimeWiring()
                .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world")))
                .build();

        SchemaGenerator schemaGenerator = new SchemaGenerator();
        GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
        // 构建一个GraphQL实例,执行graphql脚本
        GraphQL build = GraphQL.newGraphQL(graphQLSchema).build();
        ExecutionResult executionResult = build.execute("{hello}");

        System.out.println(executionResult.getData().toString());
        // Prints: {hello=world}
    }
}

这个demo直接加载了schema的内容,构建Graphql执行脚本。这种方式最接近于实际开发的操作。

Deom2

通过这个demo来看一下一个GraphQL的服务端都做了什么。并对比一下java代码加载GraphQL对象与schema中的GraphQL的联系。

所依赖的对象User

/**
 * ClassName: User<br/>
 * Description: <br/>
 * date: 2019/6/28 10:38 AM<br/>
 *
 * @author chengluchao
 * @since JDK 1.8
 */
@Data
public class User {
    private int age;
    private long id;
    private String name;
    private Card card;

    public User(int age, long id, String name, Card card) {
        this.age = age;
        this.id = id;
        this.name = name;
        this.card = card;
    }

    public User(int age, long id, String name) {
        this.age = age;
        this.id = id;
        this.name = name;
    }
}
/**
 * ClassName: Card<br/>
 * Description: <br/>
 * date: 2019/6/28 3:25 PM<br/>
 *
 * @author chengluchao
 * @since JDK 1.8
 */
@Data
public class Card {
    private String cardNumber;
    private Long userId;

    public Card(String cardNumber, Long userId) {
        this.cardNumber = cardNumber;
        this.userId = userId;
    }
}

接下来是demo

/**
 * ClassName: GraphQLDemo<br/>
 * Description: <br/>
 * date: 2019/6/28 10:40 AM<br/>
 *
 * @author chengluchao
 * @since JDK 1.8
 */

public class GraphQLDemo {
    public static void main(String[] args) {
        /*
            定义GraphQL对象,等同于schema中定义的
            type User {
                id:ID
                age:Int
                name:String
            }
        */
        GraphQLObjectType userObjectType = GraphQLObjectType.newObject()
                .name("User")
                .field(GraphQLFieldDefinition.newFieldDefinition().name("id").type(Scalars.GraphQLLong))
                .field(GraphQLFieldDefinition.newFieldDefinition().name("age").type(Scalars.GraphQLInt))
                .field(GraphQLFieldDefinition.newFieldDefinition().name("name").type(Scalars.GraphQLString))
                .build();
        /*
            queryUser : User 指定对象及参数类型
            等同于在GraphQL中定义一个无参方法 queryUser,返回值为User
            queryUser:User
            dataFetcher指定了响应的数据集,这个demo里使用了静态写入的方式
         */
        GraphQLFieldDefinition userFileldDefinition = GraphQLFieldDefinition.newFieldDefinition()
                .name("queryUser")
                .type(userObjectType)
                //静态数据
                .dataFetcher(new StaticDataFetcher(new User(19, 2, "CLC")))
                .build();
        /*
            type UserQuery 定义查询类型

            对应的graphQL为:
                type UserQuery {
                    queryUser:User
                }
         */
        GraphQLObjectType userQueryObjectType = GraphQLObjectType.newObject()
                .name("UserQuery")
                .field(userFileldDefinition)
                .build();
        /*
            Schema 定义查询
            定义了query的root类型
            对应的GraphQL语法为:
               schema {
                    query:UserQuery
               }
         */
        GraphQLSchema qlSchema = GraphQLSchema.newSchema().query(userQueryObjectType).build();

        //构建一个GraphQl对象,执行逻辑都在此处进行
        GraphQL graphQL = GraphQL.newGraphQL(qlSchema).build();

        //模拟客户端传入查询脚本,方法名queryUser,获取响应值为 id name age
        String query = "{queryUser{id name age}}";
        // 执行业务操作逻辑,获取返回值
        ExecutionResult result = graphQL.execute(query);

        System.out.println(result.toSpecification());
    }
}

这里对应的sehema应该是这样的:

#对应的User定义如下
schema {
    #定义查询
    query: UserQuery
}

#定义查询类型
type UserQuery {
    #指定对象以及参数类型
    queryUser : User
}

#定义对象
type User {
    #!表示非空
    id: ID!
    name:String
    age:Int
    card:Card
}

type Card {
    cardNumber:String
    userId:ID
}

可以看出:
schema的结构层级是:schema > UserQuery > User

schema中定义的是操作类型,UserQuery下定义的是操作方法,而User对应的是GraphQL的对象,此对象应该对应于java中一个相同的对象

以上demo实现中有两点是实战中不可取的:

  1. schema的加载方式,应该以读取本地配置的形式加载
  2. dataFetcher的数据加载,demo中是静态写死的方式,实战中应该是动态加载的数据

接下来从这两点改进

demo3

首先在resources目录下创建一个user.graphqls的文件

#对应的User定义如下
schema {
    #定义查询
    query: UserQuery
}

#定义查询类型
type UserQuery {
    #指定对象以及参数类型
    queryUser : User
    queryUserById(id:ID) : User
}

#定义对象
type User {
    #!表示非空
    id: ID!
    name:String
    age:Int
    card:Card
}

type Card {
    cardNumber:String
    userId:ID
}
import clc.bean.Card;
import clc.bean.User;
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;


/**
 * ClassName: GraphQLSDLDemo<br/>
 * Description: <br/>
 * date: 2019/6/28 11:19 AM<br/>
 *
 * @author chengluchao
 * @since JDK 1.8
 */

public class GraphQLSDLDemo {
    public static void main(String[] args) throws Exception {
        //读取graphqls文件
        String fileName = "user.graphqls";
        String fileContent = IOUtils.toString(GraphQLSDLDemo.class.getClassLoader().getResource(fileName), "UTF-8");
        //解析文件
        TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(fileContent);

        RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring()
                .type("UserQuery", builder ->
                        builder.dataFetcher("queryUserById", environment -> {
                            //解析请求参数,根据业务返回结果
                            Long id = Long.parseLong(environment.getArgument("id"));
                            Card card = new Card("123456", id);
                            return new User(18, id, "user0" + id, card);
                        })
                )
                .build();

        GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, wiring);

        GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();

        String query = "{queryUserById(id:15){id,name,age,card{cardNumber,userId}}}";
        ExecutionResult result = graphQL.execute(query);

        System.out.println("query: " + query);
        System.out.println(result.toSpecification());
    }
}

项目源码:https://gitee.com/chengluchao/graphql-clc/tree/master/graphql-java/src/main/java/com/clc/demo

从demo升级成实战项目需要做的内容:

  1. schema与java代码分离,可以通过读取文件的形式将schema加载到系统中;
  2. 动态处理GraphQL的请求参数,并随之生成对应的响应

可能大家已经发现,入参和出参都需要服务端编码实现解析。
其实graphql-java做的事情是很有限的,主要作用如下:

  1. 维护schema的结构
  2. 根据GraphQL的脚本需要,过滤响应结果

其他很大一部分的工作都需要自己实现的。

CLC

 类似资料: