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() {
@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");
}
}
};
Creating a schema using the SDL
通過SDL定義架構時,在創建可執行schema時,需要提供所需的DataFetcher和TypeResolver。
下面以名爲starWarsSchema.graphqls的靜態schema文件爲例
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定義的文件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() {
@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();
}
Creating a schema programmatically
以編程方式創建schema時,將在類型創建時提供DataFetcher和TypeResolver
Java示例如下
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();
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: 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
接口是類型的抽象定義。
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
}
當使用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
}