GraphQL实战-第二篇-java实现及分析
到这里必须具备的知识储备:对GraphQL有简单的了解,了解Schema的常用类型。
这里用一些demo示例来体验GraphQL的执行过程,这只是借助graphql-java实现的java版本。
首先需要引入graphql-java的依赖
com.graphql-java
graphql-java
15.0
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
* Description:
* date: 2019/6/28 10:38 AM
*
* @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
* Description:
* date: 2019/6/28 3:25 PM
*
* @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
* Description:
* date: 2019/6/28 10:40 AM
*
* @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实现中有两点是实战中不可取的:
schema的加载方式,应该以读取本地配置的形式加载
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
* Description:
* date: 2019/6/28 11:19 AM
*
* @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());
}
}
从demo升级成实战项目需要做的内容:
schema与java代码分离,可以通过读取文件的形式将schema加载到系统中;
动态处理GraphQL的请求参数,并随之生成对应的响应
可能大家已经发现,入参和出参都需要服务端编码实现解析。
其实graphql-java做的事情是很有限的,主要作用如下:
维护schema的结构
根据GraphQL的脚本需要,过滤响应结果
其他很大一部分的工作都需要自己实现的。
CLC