GraphQL简介

屠君墨
2023-12-01

这次的学习主要是从graphql-java文档上入手,对语言的文档有初步的认识;

GraphQL简介

在 2015 React 欧洲大会上,Lee Byron 介绍了 Facebook 的 GraphQL ,包含 GraphQL 背后的故事,查询语句的示例,还有核心的概念。GraphQL 非常易懂,直接看查询语句就能知道查询出来的数据是什么样的。你可以把 GraphQL 的查询语句想成是没有值,只有属性的对象,返回的结果就是对应的属性还有对应值的对象。 
故事

从 2011 开始,Facebook 开始越来越重视移动端,一支很小的团队开始去做 Android 与 iOS 应用。Facebook 的强项是 Web,也非常的了解 Web ,而且在这方面储备了大量的技术。当年 Facebook 的主要平台就是传统的 浏览器 Web 服务器 数据服务 的组合,Web 服务器响应浏览器的请求,到数据服务那里提供出数据,然后再交给浏览器去显示。

他们打算尽可能的使用现有的代码去实施移动端的应用,所以一开始 Facebook 的移动应用就是一个浏览器,加上了一个本地的壳,内容基本上就是简单的定制以后的移动 Web 网站。这样的好处就是可以使用所有的现有的 Web 平台上的东西。这样工程师们也可以使用平时创建东西的方法。这种方法在短时间内也得到了很大的成功,并且让公司把重点放在移动端上。

一开始都还好,不过在移动应用上添加越来越多的功能以后,就有点吃力了,移动浏览器经常会消耗掉所有的内存,让应用崩溃。另一面,在 Web 上,Facebook 仍然快速的生成,而移动端有点跟不上脚步了。这让他们决定要去做真正的本地的移动应用。

2012 年开始,Facebook 要开始开发真正的本地应用。 这跟 Web 很不一样,所以开始重新思考应用的平台。Web 就是请求一个 URL ,返回一堆 HTML。而本地移动应用,为了给应用提供需要的数据,填充数据模型 ,显示视图,要想的问题是,怎么去请求,准备,传递这些数据。而当时 Facebook 现有的服务器主要功能还是只提供 HTML。

工程师们试了一些方法,比如 RESTful API,对于 Facebook 这种复杂的应用,可能需要定义很多的端点,不同的端点返回来的数据只是略有不同,造成了资源浪费,而且还需要大量的逻辑去处理这些数据。后来他们又试了 FQL, 这是 Facebook 的公共接口,应该是一种查询语言。功能很强大,而且返回来的数据也有很好的结构。不好的地方是,查询用的语言非常难理解,比如多个 JOIN ,主键什么的,所以经常会出错。

除了这些表面上遇到的问题,工程师们也非常不喜欢这些方法表达数据的形式,比如我们平时想像的数据并不是一大堆查询语言,LEFT JOIN,RIGHT JOIN .. 也不是资源的地址。而对象的形式非常适合表达数据,一个对象,里面有一些属性,不同的属性对应不同的值。几个工程师开始了现在的 GraphQL,一种用对象,属性,关系的,有点像图形的方式来表达想要的数据。

三年前,Facebook 用了 GraphQL 做了第一款真正的本地移动应用,现在,应用每天会接受 260 亿的请求。

关于GraphQL的简介主要是从https://ninghao.net/blog/2857上复制过来的。


GraphQL-java文档相关

//以下全是个人的理解,目前只是初学,如果有错误欢迎指正。


jdk1.8以上才支持graphql-java

1、maven拉取所需要的graphql-java包,现在已经更新到3.0.0版本了

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java</artifactId>
    <version>3.0.0</version>
</dependency>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

2、这是官网给出的,也是很多博客网站给出的graphql最经典的java例子:

import graphql.GraphQL;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;

import java.util.Map;

import static graphql.Scalars.GraphQLString;
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
import static graphql.schema.GraphQLObjectType.newObject;

public class HelloWorld {

    public static void main(String[] args) {
        GraphQLObjectType queryType = newObject()
                .name("helloWorldQuery")
                .field(newFieldDefinition()
                        .type(GraphQLString)
                        .name("hello")
                        .staticValue("world"))
                .build();

        GraphQLSchema schema = GraphQLSchema.newSchema()
                .query(queryType)
                .build();

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

        Map<String, Object> result = graphQL.execute("{hello}").getData();
        System.out.println(result);
        // Prints: {hello=world}
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

3、graphql-java定义了两种schema语言,一种是标准的java语言的,一种是IDL,类似json格式的语言; 
下面是两种的示例: 
IDL(目前个人的理解是用于前端的)

type Foo {
    bar: String
}
 
 
  • 1
  • 2
  • 3

Java(目前个人理解是用于后台)

GraphQLObjectType fooType = newObject()
    .name("Foo")
    .field(newFieldDefinition()
            .name("bar")
            .type(GraphQLString))
    .build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

比较一下两种语言还是很好理解,这里需要说一下,newFieldDefinition和newObject都是静态方法,需要引入或者声明变量调用,感觉graphql中都是用引入的方法:

import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
import static graphql.schema.GraphQLObjectType.newObject;
 
 
  • 1
  • 2

方法属于哪个包中,在eclipse中可以通过ctrl+H查找;idea中还没有试过;


4、DataFetcher和TypeResolver 
DataFetcher有点不是很理解,每一个搜索域可以指定DataFetcher,如果没有指定的话,就是用默认的PropertyDataFetcher, PropertyDataFetcher主要是Map或者JavaBeans中获取数据,与Map中的Key对应,和JavaBeans中的变量名对应; 
TypeResolver就是数据的类型 
官网给出的IDL示例

schema {
    query: QueryType
}

type QueryType {
    hero(episode: Episode): Character
    human(id : String) : Human
    droid(id: ID!): Droid
}


enum Episode {
    NEWHOPE
    EMPIRE
    JEDI
}

interface Character {
    id: ID!
    name: String!
    friends: [Character]
    appearsIn: [Episode]!
}

type Human implements Character {
    id: ID!
    name: String!
    friends: [Character]
    appearsIn: [Episode]!
    homePlanet: String
}

type Droid implements Character {
    id: ID!
    name: String!
    friends: [Character]
    appearsIn: [Episode]!
    primaryFunction: String
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

Java代码 
目前这里面的内容不懂的部分还是很多的 
①loadSchema这个方法是在哪里定义的至今没有找到; 
②starWarsSchema.graphqls这个文件怎么定义的?里面的内容是什么; 
③StarWarsData理解上应该是一个entity类,但是为什么没有申明变量就可以直接使用里面getter和setter,还是需要申明了,但是文档觉得没有必要给出这部分代码; 
④CustomScalar定义在哪里?

SchemaParser schemaParser = new SchemaParser();
SchemaGenerator schemaGenerator = new SchemaGenerator();

File schemaFile = loadSchema("starWarsSchema.graphqls");

TypeDefinitionRegistry typeRegistry = schemaParser.parse(schemaFile);
RuntimeWiring wiring = buildRuntimeWiring();
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring);



RuntimeWiring buildRuntimeWiring() {
    return RuntimeWiring.newRuntimeWiring()
            .scalar(CustomScalar)
            // this uses builder function lambda syntax
            .type("QueryType", typeWiring -> typeWiring
                    .dataFetcher("hero", new StaticDataFetcher(StarWarsData.getArtoo()))
                    .dataFetcher("human", StarWarsData.getHumanDataFetcher())
                    .dataFetcher("droid", StarWarsData.getDroidDataFetcher())
            )
            .type("Human", typeWiring -> typeWiring
                    .dataFetcher("friends", StarWarsData.getFriendsDataFetcher())
            )
            // you can use builder syntax if you don't like the lambda syntax
            .type("Droid", typeWiring -> typeWiring
                    .dataFetcher("friends", StarWarsData.getFriendsDataFetcher())
            )
            // or full builder syntax if that takes your fancy
            .type(
                    newTypeWiring("Character")
                            .typeResolver(StarWarsData.getCharacterTypeResolver())
                            .build()
            )
            .build();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

官网还给出了另外一种buildDynamicRuntimeWiring,根据名字为动态的RuntimeWiring,但是里面的方法也不是很懂,有人懂得望告知:

RuntimeWiring buildDynamicRuntimeWiring() {
    WiringFactory dynamicWiringFactory = new WiringFactory() {
        @Override
        public boolean providesTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) {
            return getDirective(definition,"specialMarker") != null;
        }

        @Override
        public boolean providesTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) {
            return getDirective(definition,"specialMarker") != null;
        }

        @Override
        public TypeResolver getTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) {
            Directive directive  = getDirective(definition,"specialMarker");
            return createTypeResolver(definition,directive);
        }

        @Override
        public TypeResolver getTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) {
            Directive directive  = getDirective(definition,"specialMarker");
            return createTypeResolver(definition,directive);
        }

        @Override
        public boolean providesDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) {
            return getDirective(definition,"dataFetcher") != null;
        }

        @Override
        public DataFetcher getDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) {
            Directive directive = getDirective(definition, "dataFetcher");
            return createDataFetcher(definition,directive);
        }
    };
    return RuntimeWiring.newRuntimeWiring()
            .wiringFactory(dynamicWiringFactory).build();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

5、类型: 
GraphQL提供一下类型: 
- Scalar 
- Object 
- Interface 
- Union 
- InputObject 
- Enum

Scalar 
- GraphQLString 
- GraphQLBoolean 
- GraphQLInt 
- GraphQLFloat 
- GraphQLID 
- GraphQLLong 
- GraphQLShort 
- GraphQLByte 
- GraphQLFloat 
- GraphQLBigDecimal 
- GraphQLBigInteger

Object

type SimpsonCharacter {
    name: String
    mainCharacter: Boolean
}
 
 
  • 1
  • 2
  • 3
  • 4
GraphQLObjectType simpsonCharacter = newObject()
.name("SimpsonCharacter")
.description("A Simpson character")
.field(newFieldDefinition()
        .name("name")
        .description("The name of the character.")
        .type(GraphQLString))
.field(newFieldDefinition()
        .name("mainCharacter")
        .description("One of the main Simpson characters?")
        .type(GraphQLBoolean))
.build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Interface

interface ComicCharacter {
    name: String;
}
 
 
  • 1
  • 2
  • 3
GraphQLInterfaceType comicCharacter = newInterface()
    .name("ComicCharacter")
    .description("A abstract comic character.")
    .field(newFieldDefinition()
            .name("name")
            .description("The name of the character.")
            .type(GraphQLString))
    .build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Union

interface ComicCharacter {
    name: String;
}
 
 
  • 1
  • 2
  • 3
GraphQLUnionType PetType = newUnionType()
    .name("Pet")
    .possibleType(CatType)
    .possibleType(DogType)
    .typeResolver(new TypeResolver() {
        @Override
        public GraphQLObjectType getType(TypeResolutionEnvironment env) {
            if (env.getObject() instanceof Cat) {
                return CatType;
            }
            if (env.getObject() instanceof Dog) {
                return DogType;
            }
            return null;
        }
    })
    .build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

Enum

enum Color {
    RED
    GREEN
    BLUE
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
GraphQLEnumType colorEnum = newEnum()
    .name("Color")
    .description("Supported colors.")
    .value("RED")
    .value("GREEN")
    .value("BLUE")
    .build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ObjectInputType

input Character {
    name: String
}
 
 
  • 1
  • 2
  • 3
GraphQLInputObjectType inputObjectType = newInputObject()
    .name("inputObjectType")
    .field(newInputObjectField()
            .name("field")
            .type(GraphQLString))
    .build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6、递归类型 
每个人都是有朋友的,每个人的朋友都是人,然后每个人又都有朋友,朋友又会有朋友的朋友,就是一直递归下去,graphql-java不会出现这种情况

GraphQLObjectType person = newObject()
    .name("Person")
    .field(newFieldDefinition()
            .name("friends")
            .type(new GraphQLList(new GraphQLTypeReference("Person"))))
    .build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

7、对于查询的类可进行扩展 
java代码上是这样是实现的

SchemaParser schemaParser = new SchemaCompiler();
SchemaGenerator schemaGenerator = new SchemaGenerator();

File schemaFile1 = loadSchema("starWarsSchemaPart1.graphqls");
File schemaFile2 = loadSchema("starWarsSchemaPart2.graphqls");
File schemaFile3 = loadSchema("starWarsSchemaPart3.graphqls");

TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();

// each registry is merged into the main registry
typeRegistry.merge(schemaParser.compile(schemaFile1));
typeRegistry.merge(schemaParser.compile(schemaFile2));
typeRegistry.merge(schemaParser.compile(schemaFile3));

GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, buildRuntimeWiring());
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在IDL中大概就是下面这种情况

type Human {
    id: ID!
    name: String!
}
#Another part of your system can extend this type to add more shape to it.
extend type Human implements Character {
    id: ID!
    name: String!
    friends: [Character]
    appearsIn: [Episode]!
}
#You can have as many extensions as you think sensible. 
extend type Human {
    homePlanet: String
}

#With all these type extensions in place the Human type now looks like this at runtime.
type Human implements Character {
    id: ID!
    name: String!
    friends: [Character]
    appearsIn: [Episode]!
    homePlanet: String
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

今天的工作暂时就这么多,明天研究研究graphql-java源代码,再看看IDL语言,看看能不能对graphq-java有更深的了解,目前了解的情况还无法根本无法用来写代码。

 类似资料: