zoukankan      html  css  js  c++  java
  • GraphQL Java

    Schema

    创建一个schema

    GraphQL API具有一个Schema,该Schema定义了可以Query(查询)或Mutation(变更)的每个字段以及这些字段的类型。

    graphql-java提供了两种不同的定义schema的方式:编程方式编写,和使用graphql dsl语法(也称为SDL)编写。

    例如:

    SDL示例:

        type Foo {
            bar: String
        }
    

    Java代码示例:

        GraphQLObjectType fooType = newObject()
            .name("Foo")
            .field(newFieldDefinition()
                    .name("bar")
                    .type(GraphQLString))
            .build();
    

    DataFetcher和TypeResolver

    DataFetcher用于获取字段(field)对应的数据。另外,如果是Mutation(变更)类型,则可用于更新数据。

    GraphQL中的每个字段(Field Definition)都有一个DataFetcher。如果未指定DataFetcher,则该字段启用默认的PropertyDataFetcher。

    PropertyDataFetcher从Map和Java Bean中获取数据。当字段名称与Map中的key或bean对象的属性相同时,无需显式指定DataFetcher。

    TypeResolver(类型解析器)用于帮助graphql-java判断数据的实际类型。例如对于Interface和Union类型,TypeResolver用于确定最终获取到的对象属于Interface(接口)的哪个实现,或Union(联合)中的哪种具体类型。

    例如,假定你有一个Interface类型叫做MagicUserType,有一系列实现该接口的具体类型:Wizard、Witch和Necomancer。TypeResolver(类型解析器)用于在运行时识别出数据的具体类型(Type),进而决定调用哪个DataFetcher和字段。

            new TypeResolver() {
                @Override
                public GraphQLObjectType getType(TypeResolutionEnvironment env) {
                    Object javaObject = env.getObject();
                    if (javaObject instanceof Wizard) {
                        return env.getSchema().getObjectType("WizardType");
                    } else if (javaObject instanceof Witch) {
                        return env.getSchema().getObjectType("WitchType");
                    } else {
                        return env.getSchema().getObjectType("NecromancerType");
                    }
                }
            };
    

    使用SDL创建一个schema

    通过SDL定义模式时,需提供DataFetcher和TypeResolver。

    例如,对于如下的schema定义:(starWarsSchema.graphqls)

        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
        }
    

    这个schema定义包含了字段(field)和类型(type)定义,但是仍需要一个“运行时绑定”(runtime wiring),将它绑定到Java方法中,使它成为一个完全可执行的schema。

    可以使用如下的代码完成绑定(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();
        }
    

    最后,你需要通过将schema文件和“绑定”(wiring)结合,创建一个可执行的schema。示例如下:

            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);
    

    除了使用上面的build方式之外,TypeResolver和DataFetcher也可以使用WiringFactory接口完成绑定。

    示例代码如下:

        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();
        }
    

    编程式构建schema

    以编程方式创建模式时,将在创建类型时提供DataFetcher和TypeResolver:

    示例代码如下:

            DataFetcher<Foo> fooDataFetcher = new DataFetcher<Foo>() {
                @Override
                public Foo get(DataFetchingEnvironment environment) {
                    // environment.getSource() is the value of the surrounding
                    // object. In this case described by objectType
                    Foo value = perhapsFromDatabase(); // Perhaps getting from a DB or whatever
                    return value;
                }
            };
    
            GraphQLObjectType objectType = newObject()
                    .name("ObjectType")
                    .field(newFieldDefinition()
                            .name("foo")
                            .type(GraphQLString)
                    )
                    .build();
    
            GraphQLCodeRegistry codeRegistry = newCodeRegistry()
                    .dataFetcher(
                            coordinates("ObjectType", "foo"),
                            fooDataFetcher)
                    .build();
    

    类型(Type)

    Graphql类型系统支持如下几种类型

    • Scalar
    • Object
    • Interface
    • Union
    • InputObject
    • Enum

    Scalar

    graphql-java支持如下的Scalars:

    1. 标准的graphql scalars:GraphQLString、GraphQLBoolean、GraphQLInt、GraphQLFloat、GraphQLID

    2. graph-java扩展的Scalar:

      • GraphQLLong
      • GraphQLShort
      • GraphQLByte
      • GraphQLFloat
      • GraphQLBigDecimal
      • GraphQLBigInteger

      注意,扩展的标量的语义,可能无法被graphql的客户端所正确理解。例如,将Java Lang(最大值263-1)转换为JavaScript数字(最大值253-1),可能会产生问题。

    Object

    SDL示例如下:

        type SimpsonCharacter {
            name: String
            mainCharacter: Boolean
        }
    
    

    Java示例如下:

        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();
    
    

    Interface

    Interface是抽象类型的定义。

    SDL示例如下:

        interface ComicCharacter {
            name: String;
        }
    
    

    Java示例如下:

        GraphQLInterfaceType comicCharacter = newInterface()
            .name("ComicCharacter")
            .description("An abstract comic character.")
            .field(newFieldDefinition()
                    .name("name")
                    .description("The name of the character.")
                    .type(GraphQLString))
            .build();
    
    

    Union

    SDL示例如下:

        type Cat {
            name: String;
            lives: Int;
        }
    
        type Dog {
            name: String;
            bonesOwned: int;
        }
    
        union Pet = Cat | Dog
    
    

    Java示例如下:

            TypeResolver 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;
                }
            };
            GraphQLUnionType PetType = newUnionType()
                    .name("Pet")
                    .possibleType(CatType)
                    .possibleType(DogType)
                    .build();
    
            GraphQLCodeRegistry codeRegistry = newCodeRegistry()
                    .typeResolver("Pet", typeResolver)
                    .build();
    
    

    Enum

    SDL示例:

        enum Color {
            RED
            GREEN
            BLUE
        }
    
    

    Java示例如下:

        GraphQLEnumType colorEnum = newEnum()
            .name("Color")
            .description("Supported colors.")
            .value("RED")
            .value("GREEN")
            .value("BLUE")
            .build();
    
    

    ObjectInputType

    SDL示例:

        input Character {
            name: String
        }
    
    

    Java示例如下:

        GraphQLInputObjectType inputObjectType = newInputObject()
            .name("inputObjectType")
            .field(newInputObjectField()
                    .name("field")
                    .type(GraphQLString))
            .build();
    
    

    Type References(类型引用,可用于创建递归类型)

    GraphQL支持递归类型。例如,Person类可能包含一系列相同类型的friends。

    为了支持这样的类型,graphql-java提供了GraphQLTypeReference类。

    当schema被创建时,GraphQLTypeReference会使用替换为真实的类型。

    例如:

        GraphQLObjectType person = newObject()
                .name("Person")
                .field(newFieldDefinition()
                        .name("friends")
                        .type(GraphQLList.list(GraphQLTypeReference.typeRef("Person"))))
                .build();
    
    

    如果schema使用SDL创建,name递归类型无需被显示处理。graphql会自动检测出来。

    Schema SDL模块化

    维护一个较大的schema文件不是可行的,graphql-java也提供了两种方式,可以针对schema进行模块化。

    第一种方法是将多个Schema SDL文件合并为一个逻辑单元。 在下面的情况下,Schema拆分为多个文件,并在Schema生成之前合并在一起。

        SchemaParser schemaParser = new SchemaParser();
        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.parse(schemaFile1));
        typeRegistry.merge(schemaParser.parse(schemaFile2));
        typeRegistry.merge(schemaParser.parse(schemaFile3));
    
        GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, buildRuntimeWiring());
    
    

    Graphql SDL类型系统具有另一种方法用于模块化模式的构造。 可以使用类型扩展来为类型添加额外的字段和接口。

    假设在一个模式文件中以这样的类型开始:

        type Human {
            id: ID!
            name: String!
        }
    
    

    系统中的另一部分可以对这个类型进行扩展,并且增加更多的字段。例如:

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

    可以使用尽可能多的扩展。它们将会以被发现的顺序进行合并。重复的字段将会被合并为一个。

        extend type Human {
            homePlanet: String
        }
    
    

    以上的多个schema文件,在运行时合并为一个Human类型,如下:

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

    这在schema的顶层设计时十分重要。你可以使用扩展类型,来为顶层的schema中的”query“添加新的字段。

    团队可以为顶层的graphql查询独立的进行各自的模块功能实现。

        schema {
          query: CombinedQueryFromMultipleTeams
        }
    
        type CombinedQueryFromMultipleTeams {
            createdTimestamp: String
        }
    
        # maybe the invoicing system team puts in this set of attributes
        extend type CombinedQueryFromMultipleTeams {
            invoicing: Invoicing
        }
    
        # and the billing system team puts in this set of attributes
        extend type CombinedQueryFromMultipleTeams {
            billing: Billing
        }
    
        # and so and so forth
        extend type CombinedQueryFromMultipleTeams {
            auditing: Auditing
        }
    
    

    订阅支持

    订阅允许你进行查询,并且当相关查询的后端对象有所变更时,更新后的对象会实时推送过来。

        subscription foo {
            # normal graphql query
        }
    
    
  • 相关阅读:
    一网友推荐的书:框架设计(第2版):CLR Via C#
    线程与WinForm设计,防冻结,卡住窗体
    通用数据库操作辅助类DbHelper
    高级着色器语言(High Level Shader Language,简称HLSL)
    无法将 匿名方法 转换为类型“System.Delegate”,因为它不是委托类型
    根据RGB,计算灰度值
    温故而知新:WinForm/Silverlight多线程编程中如何更新UI控件的值
    [原创视频]PHP在netbeans中的简单使用
    自己封装的ASP.NET的SQLITE数据库的操作类
    《JavaScript征途》读后感
  • 原文地址:https://www.cnblogs.com/pku-liuqiang/p/11498362.html
Copyright © 2011-2022 走看看