当前位置: 代码迷 >> 综合 >> graphql-java(0)如何定义Schema
  详细解决方案

graphql-java(0)如何定义Schema

热度:32   发布时间:2023-09-23 10:22:44.0

https://dreamylost.cn/
文档 version = 1.4 仅供参考

Creating a schema

定义数据模型

GraphQL API具有一个schema,该schema定义了可以查询或突变的每个字段以及这些字段的类型。

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

如果不确定要使用哪种方式,我们建议使用SDL。

SDL 示例

type Foo {bar: String
}

Java 示例

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

DataFetcher and TypeResolver

定义数据读取器和类型解析器

DataFetcher提供字段的数据(如果是突变的话,并更改某些内容)。

GraphQL定义了两种请求类型,查询query和突变mutation(突变包含新增、删除、修改)。

每个字段定义都有一个DataFetcher。如果未配置,则使用PropertyDataFetcher,这是一个默认的读取器。
PropertyDataFetcher从Map和Java Bean获取数据。因此,当字段名称与Map关键字或源Object的属性名称匹配时,就不需要DataFetcher。

TypeResolver帮助graphql-java决定具体值属于哪种类型。Interface和Union类型需要此功能。

例如,假设您有一个名为MagicUserType的Interface,该接口可解析回一系列名为Wizard,Witch和Necromancer的Java类。类型解析器负责检查运行时对象,并确定应使用什么GraphqlObjectType来表示该对象,从而决定要调用哪些数据读取程序和字段。

一个常见的实现如下

new TypeResolver() {
    @Overridepublic 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");}}
};

Creating a schema using the SDL

通过SDL定义架构时,在创建可执行schema时,需要提供所需的DataFetcher和TypeResolver。

下面以名为starWarsSchema.graphqls的静态schema文件为例

schema {query: QueryType
}type QueryType {# !表示不可为空,否则报错hero(episode: Episode): Characterhuman(id : String) : Humandroid(id: ID!): Droid
}enum Episode {NEWHOPEEMPIREJEDI
}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定义的文件starWarsSchema.graphqls,包含字段和类型定义,但是您需要运行时织入才能使其成为真正的可执行schema。

运行时连接包含DataFetcher,TypeResolvers和自定义Scalar,它们是制作完全可执行的schema所必需的。

您可以使用以下建造者模式将其连接在一起

RuntimeWiring buildRuntimeWiring() {
    return RuntimeWiring.newRuntimeWiring().scalar(CustomScalar)//数据读取器.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())).type("Droid", typeWiring -> typeWiring.dataFetcher("friends", StarWarsData.getFriendsDataFetcher()))//类型解析器.type(newTypeWiring("Character").typeResolver(StarWarsData.getCharacterTypeResolver()).build()).build();
}

最后,您可以通过连接静态schema和运行时织入对象(RuntimeWiring),生成可执行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);

除了上面显示的建造器风格外,还可以使用WiringFactory接口来连接TypeResolver和DataFetcher。由于可以检查SDL定义以确定要连接的内容,因此可以进行更动态的运行时连接。例如,您可以查看SDL指令或SDL定义的其他方面,以帮助您确定要创建的运行时。

RuntimeWiring buildDynamicRuntimeWiring() {
    WiringFactory dynamicWiringFactory = new WiringFactory() {
    @Overridepublic boolean providesTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) {
    return getDirective(definition,"specialMarker") != null;}@Overridepublic boolean providesTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) {
    return getDirective(definition,"specialMarker") != null;}@Overridepublic TypeResolver getTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) {
    Directive directive  = getDirective(definition,"specialMarker");return createTypeResolver(definition,directive);}@Overridepublic TypeResolver getTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) {
    Directive directive  = getDirective(definition,"specialMarker");return createTypeResolver(definition,directive);}@Overridepublic boolean providesDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) {
    return getDirective(definition,"dataFetcher") != null;}@Overridepublic DataFetcher getDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) {
    Directive directive = getDirective(definition, "dataFetcher");return createDataFetcher(definition,directive);}};return RuntimeWiring.newRuntimeWiring().wiringFactory(dynamicWiringFactory).build();
}

Creating a schema programmatically

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

Java示例如下

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

Types

GraphQL类型系统支持以下类型

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

Scalar

graphql-java支持以下标量

标准graphql标量

  • *GraphQLString
  • *GraphQLBoolean
  • *GraphQLInt
  • *GraphQLFloat
  • *GraphQLID

扩展graphql-java标量

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

请注意,客户可能无法理解扩展标量范围的语义。例如,将Java Long(最大值2^63-1)映射到JavaScript Number(最大值2^53-1)对您来说可能有问题。

Object

SDL 示例

type SimpsonCharacter {name: StringmainCharacter: 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

接口是类型的抽象定义。

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() {
    @Overridepublic 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 {REDGREENBLUE
}

Java 示例

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

ObjectInputType

SDL 示例

input Character {name: String
}

当使用graphql做突变操作时就需要input。(类似restful中定义request body)
Java 示例

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

Type References (recursive types)

GraphQL支持递归类型:例如,一个Person可以包含相同类型的朋友列表。

为了能够声明这种类型,graphql-java具有GraphQLTypeReference类。

创建schema时,GraphQLTypeReference将替换为实际类型对象。

示例如下

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

通过SDL声明schema时,不需要为递归类型进行特殊处理,因为它可以为您检测到并自动完成。

Modularising the Schema SDL

拥有一个大的schema文件并不总是可行的。您可以使用两种技术对模式进行模块化。

第一种技术是将多个Schema SDL文件合并到一个逻辑单元中。在下面的情况下,在生成模式之前,已将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();//合并
typeRegistry.merge(schemaParser.parse(schemaFile1));
typeRegistry.merge(schemaParser.parse(schemaFile2));
typeRegistry.merge(schemaParser.parse(schemaFile3));GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, buildRuntimeWiring());

Graphql SDL类型系统具有用于将schema模块化的另一种方法。您可以使用type extensions将其他字段和接口添加到类型。

假设您在一个schema文件中有这样的类型定义

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

系统的另一部分可以扩展此类型以为其添加更多形状。

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

您可以根据需要选择任意数量的扩展名。它们将按照遇到的顺序组合在一起。重复的字段将合并为一个(但是不允许将字段重新定义为新类型)。

extend type Human {homePlanet: String
}

有了所有这些类型扩展后,Human类型现在在运行时看起来像这样。

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

这在最高层尤其有用。您可以使用扩展类型向顶层schema“query”添加新字段。团队可以贡献“sections”来提供总的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 Support

订阅使您可以执行查询,并且只要该查询的支持对象发生更改,就会发送更新。

subscription foo {# normal graphql query
}
  相关解决方案