Skip to content
标签
接口规范
字数
1860 字
阅读时间
9 分钟

一、概述

GraphQL 是由 Facebook 创造的用于描述复杂数据模型的一种查询语言。这里查询语言所指的并不是常规意义上的类似 sql 语句的查询语言,而是一种用于前后端数据查询方式的规范。

Restful存在的问题:

存在多余字段或多次请求的情况

GraphQL 特点:

  • 按需索取数据,避免浪费
  • 一次查询多个数据
  • API的演进无需划分版本

二、查询规范

GraphQL定义了一套规范,用来描述语法定义,具体参考:http://graphql.cn/learn/queries/ http://graphql.cn/learn/queries/

2.1 字段(Fields)

在GraphQL的查询中,请求结构中包含了所预期结果的结构,这个就是字段。并且响应的结构和请求结构基本一致,这是GraphQL的一个特性,这样就可以让请求发起者很清楚的知道自己想要什么。

2.2 参数(Arguments)

在查询数据时,离不开传递参数,在GraphQL的查询中,也是可以传递参数的,语法:(参数名:参数值)

2.3 别名(Aliases)

如果一次查询多个相同对象,但是值不同,这个时候就需要起别名了,否则json的语法就不能通过

2.4 片段(Fragments)

查询对的属相如果相同,可以采用片段的方式进行简化定义。

三、Schema 和类型规范

Schema 是用于定义数据结构的,比如说,User对象中有哪些属性,对象与对象之间是什么关系等。

3.1 定义结构

schema { #定义查询
query: UserQuery
}
type UserQuery { #定义查询的类型
user(id:ID) : User #指定对象以及参数类型
}
type User { #定义对象
id:ID! # !表示该属性是非空项
name:String
age:Int
}

3.2 标量类型(Scalar Types)

GraphQL规范中,默认定义了5种类型:

  • Int :有符号 32 位整数。
  • Float :有符号双精度浮点值。
  • String :UTF‐8 字符序列。
  • Boolean : true 或者 false 。
  • ID :ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。

3.3 枚举类型

枚举类型是一种特殊的标量,它限制在一个特殊的可选值集合内。

enum Episode { #定义枚举
NEWHOPE
EMPIRE
JEDI
}
type Human {
id: ID!
name: String!
appearsIn: [Episode]! #使用枚举类型
homePlanet: String
}

3.4 接口(interface)

,GraphQL 支持接口。一个接口是一个抽象类型,它包含某些字段,而对象类型必须包含这些字段,才能算实现了这个接口。

interface Character { #定义接口
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
#实现接口
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}

四、java实现

官方只是定义了规范并没有做实现,需要有第三方来进行实现了,关于GraphQL的java实现有几种,使用官方推荐的实现:graphql-java,通过该实现就可以编写GraphQL的服务端了。 官网:https://www.graphql-java.com/ github:https://github.com/graphql-java/graphql-java

4.1 项目准备

pom依赖

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

graphql-java包并没有发布到maven中央仓库,需要配置第三方仓库才能使用。

xml
<profile>
	<id>bintray</id>
	<repositories>
		<repository>
			<id>bintray</id>
			<url>http://dl.bintray.com/andimarek/graphql-java</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>bintray</id>
			<url>http://dl.bintray.com/andimarek/graphql-java</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>
</profile>

创建对象

java
public class User {
    private Long id;
	private String name;
	private Integer age;
	public User() {
	}
    public User(Long id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public Long getId() {
    	return id;
    }
    public void setId(Long id) {
    	this.id = id;
    }
    public String getName() {
    	return name;
    }
    public void setName(String name) {
    	this.name = name;
    }
    public Integer getAge() {
    	return age;
    }
    public void setAge(Integer age) {
    	this.age = age;
    }
}

编写查询User对象实现

#对应的User定义如下
schema { #定义查询
query: UserQuery
}
type UserQuery { #定义查询的类型
user : User #指定对象以及参数类型
}
type User { #定义对象
id:Long! # !表示该属性是非空项
name:String
age:Int
}

java实现

java
public class GraphQLDemo {
    /**
    * 定义Schema
    * <p>
    * schema { #定义查询
    * query: UserQuery
    * }
    *
    * @return
    */
    public static GraphQLSchema createGraphqlSchema(GraphQLFieldDefinition userDefinition) {
    	GraphQLObjectType userQuery = newObject().name("userQuery").field(userDefinition).build();
    	return GraphQLSchema.newSchema().query(userQuery).build();
    }
    /**
    * 定义查询的类型
    * <p>
    * type UserQuery { #定义查询的类型
    * user : User #指定对象
    * }
    *
    * @return
    */
	public static GraphQLFieldDefinition createUserDefinition(GraphQLObjectType userType) {
		return newFieldDefinition().name("User").type(userType)
			//静态数据
			.dataFetcher(new StaticDataFetcher(new User(1L, "张三", 20))).build();
	}
    /**
    * 定义User对象类型
    *
    * type User { #定义对象
    * id:Long! # !表示该属性是非空项
    * name:String
    * age:Int
    * }
    *
    *
    * @return
    */
	public static GraphQLObjectType createUserObjectType(){
		return newObject().name("User").field(newFieldDefinition().name("id").type(GraphQLLong)).field(newFieldDefinition().name("name").type(GraphQLString)).field(newFieldDefinition().name("age").type(GraphQLInt)).build();
	}
	public static void main(String[] args) {
        GraphQLObjectType userObjectType = createUserObjectType();
        GraphQLFieldDefinition userDefinition =
        createUserDefinition(userObjectType);
        GraphQL graphQL = GraphQL.newGraphQL(createGraphqlSchema(userDefinition)).build();
        String query = "{User{id,name}}";
        ExecutionResult executionResult = graphQL.execute(query);
        // 打印错误
        System.out.println("错误:" + executionResult.getErrors());
        // 打印数据
        System.out.println("结果:" +(Object) executionResult.getData());
	}
}

4.2 设置查询参数

java
/**
* 定义查询的类型
* <p>
* type UserQuery { #定义查询的类型
* user : User #指定对象
* }
*
* @return
*/
public static GraphQLFieldDefinition createUserDefinition(GraphQLObjectType userType) {
	return newFieldDefinition().name("User").type(userType)
		//静态数据
		// .dataFetcher(new StaticDataFetcher(new User(1L, "张三", 20)))
		// 设置参数
		.argument(newArgument().name("id").type(GraphQLLong).build()).dataFetcher(environment -> {
		Long id = environment.getArgument("id");
		return new User(id, "张三_"+id, 20 + id.intValue());
	}).build();
}

4.3 使用SDL构建schema

graphql-java 提供了两种不同的方式来定义模式:以编程方式作为Java代码或通过特殊的graphql dsl(称为SDL)。

在resources目录下创建user.graphqls文件

schema {
query: UserQuery
}
type UserQuery {
user(id:Long) : User
}
type User {
id:Long!
name:String
age:Int
}

idea 安装GraphQL插件

构建schema

java
public class GraphQLSDLDemo {
    /**
    * 定义Schema
    * <p>
    * schema { #定义查询
    * query: UserQuery
    * }
    *
    * @return
    */
    public static GraphQLSchema createGraphqlSchema(TypeDefinitionRegistry typeRegistry, RuntimeWiring wiring) {
		SchemaGenerator schemaGenerator = new SchemaGenerator();
		return schemaGenerator.makeExecutableSchema(typeRegistry, wiring);
	}
    /**
    * 定义类型的注册器
    *
    * @param fileContent
    * @return
    */
	public static TypeDefinitionRegistry createTypeDefinitionRegistry(String fileContent){
		SchemaParser schemaParser = new SchemaParser();
		return schemaParser.parse(fileContent);
	}
    /**
    * 读取文件内容
    *
    * @param fileName
    * @return
    */
	public static String readFileToString(String fileName){
		try {
			return IOUtils.toString(GraphQLSDLDemo.class.getClassLoader().getResourceAsStream(fileName), "UTF-8");
          } catch (IOException e) {
			e.printStackTrace();
        }
		return null;
	}
	public static RuntimeWiring createRuntimeWiring() {
		return RuntimeWiring.newRuntimeWiring().type("UserQuery", typeWiring -> typeWiring.dataFetcher("user", environment -> {
			Long id = environment.getArgument("id");
			return new User(id, "张三_"+id, 20 + id.intValue());
			})).build();
    }
	public static void main(String[] args) {
        String fileName = "user.graphqls";
        TypeDefinitionRegistry registry = createTypeDefinitionRegistry(readFileToString(fileName));
        RuntimeWiring runtimeWiring = createRuntimeWiring();
		GraphQL graphQL = GraphQL.newGraphQL(createGraphqlSchema(registry,runtimeWiring)).build();
		// 注意:查询语句中的user是小写,要和user.graphqls文件中的属性名一致
		String query = "{user(id:1){id,name,age}}";
		ExecutionResult executionResult = graphQL.execute(query);
		System.out.println("查询字符串:" + query);
		// 打印错误
		System.out.println("错误:" + executionResult.getErrors());
		// 打印数据
		System.out.println("结果:" +(Object) executionResult.getData());
	}
}

4.4 对象嵌套

创建Card对象:

java
public class Card {
	private String cardNumber;
	private Long userId;
    public Card() {
    }
	public Card(String cardNumber, Long userId) {
		this.cardNumber = cardNumber;
		this.userId = userId;
	}
	public String getCardNumber() {
		return cardNumber;
	}
	public void setCardNumber(String cardNumber) {
		this.cardNumber = cardNumber;
	}
	public Long getUserId() {
		return userId;
	}
	public void setUserId(Long userId) {
		this.userId = userId;
	}
}

user中包含card对象

private Card card;

user.graphqls文件

schema {
query: UserQuery
}
type UserQuery {
user(id:Long) : User
}
type User {
id:Long!
name:String
age:Int
card:Card
}
type Card {
cardNumber:String!
userId:Long
}

修改GraphQLSDLDemo逻辑

java
public static RuntimeWiring createRuntimeWiring() {
	return RuntimeWiring.newRuntimeWiring().type("UserQuery", typeWiring -> typeWiring.dataFetcher("user", environment -> {
		Long id = environment.getArgument("id");
		return new User(id, "张三_"+id, 20 + id.intValue(),new
		Card("number_"+id, id));
		})
	).build();
}

GraphQL并不实现关联查询,需要自己实现。