java s:schema_GraphQL Java - Schema

江华容
2023-12-01

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 fooDataFetcher = new DataFetcher() {

@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:

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

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

}

 类似资料: