原文:http://blog.mygraphql.com/wordpress/?p=100
創(chuàng)建Schema
Schema的主要用途是定義所有可供查詢(xún)的字段(field),它們最終組合成一套完整的GraphQL
API.
“graphql-java”提供兩種方法來(lái)定義Schema凌唬。用java代碼來(lái)定義幻妓、用GraphQL
SDL(即IDL)來(lái)定義。
注意:SDL(IDL)現(xiàn)在還不是 官方 graphql 規(guī)范. 本GraphQL實(shí)現(xiàn)忱嘹,是基于
已有的JS參考實(shí)現(xiàn)
來(lái)開(kāi)發(fā)的。但JS參考實(shí)現(xiàn)中的很多代碼也是基于SDL(IDL)語(yǔ)法的,所以你可以認(rèn)為這語(yǔ)法是可以長(zhǎng)期使用的.
如果你不確認(rèn)用“java代碼”還是用“GraphQL
SDL(即IDL)”來(lái)定義你的Schema仍律,那么我們建議你用SDL(IDL)
SDL example:
type Foo {
bar: String
}
java代碼例子:
GraphQLObjectType fooType = newObject()
.name("Foo")
.field(newFieldDefinition()
.name("bar")
.type(GraphQLString))
.build();
DataFetcher 與 TypeResolver
對(duì)象 DataFetcher
作用是獲取字段(field)對(duì)應(yīng)的數(shù)據(jù);另外实柠,在修改(mutation)操作時(shí)水泉,可以更新數(shù)據(jù)
每個(gè)字段都有自己的 DataFetcher
. 如果未為字段指定DataFetcher,
那么自動(dòng)使用默認(rèn)的 PropertyDataFetcher .
PropertyDataFetcher
從 Map
和 Java Beans 中獲取數(shù)據(jù).
所以,當(dāng)Schema中的field名窒盐,與Map中的key值草则,或 Source Object
中的 java
bean 字段名相同時(shí),不需要為field指定 DataFetcher
.
對(duì)象 TypeResolver
幫助 graphql-java
判斷數(shù)據(jù)的實(shí)際類(lèi)型(type). 所以
Interface
和 Union
均需要指定關(guān)聯(lián)的 TypeResolver(類(lèi)型識(shí)別器)
.
例如蟹漓,你有一個(gè) Interface
叫 MagicUserType
它有可能是以下的具體類(lèi)型(Type) Wizard, Witch and Necromancer.
Type resolver(類(lèi)型識(shí)別器) 的作用是在運(yùn)行時(shí)識(shí)別出 GraphqlObjectType
的具體類(lèi)型(Type)炕横。后期具體類(lèi)型下的field相關(guān)的 data
fetcher被調(diào)用并獲取數(shù)據(jù).
new TypeResolver() {
@Override
public GraphQLObjectType getType(TypeResolutionEnvironment env) {
Object javaObject = env.getObject();
if (javaObject instanceof Wizard) {
return (GraphQLObjectType) env.getSchema().getType("WizardType");
} else if (javaObject instanceof Witch) {
return (GraphQLObjectType) env.getSchema().getType("WitchType");
} else {
return (GraphQLObjectType) env.getSchema().getType("NecromancerType");
}
}
};
用 SDL 創(chuàng)建 Schema
當(dāng)使用SDL方法來(lái)開(kāi)發(fā)時(shí),你需要同時(shí)編寫(xiě)對(duì)應(yīng)的 DataFetcher
和
TypeResolver
葡粒。
很大的 Schema 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
}
由于Schema中只是指定了靜態(tài)的字段和類(lèi)型,你還需要把它綁定到j(luò)ava方法中嗽交。以讓Schema可以運(yùn)行起來(lái)
這里的綁定卿嘲,包括 DataFetcher
, TypeResolvers
與自定義 Scalar
.
用下頁(yè)的Builder方法,就可以綁定Schema和Java程序
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();
}
最后轮纫,你可以通過(guò)整合靜態(tài) Schema 和 綁定(wiring)腔寡,而生成一個(gè)可以執(zhí)行的
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);
除了上面的 builder 風(fēng)格, TypeResolver
s 與 DataFetcher
s 也可以通過(guò)
WiringFactory
接口綁定在一起掌唾。通過(guò)程序去分析 SDL
放前,就可以允許更自由的綁定忿磅。你可以 通過(guò)分析 SDL 聲明, 或其它 SDL
定義去決定你的運(yùn)行時(shí)邏輯。
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();
}
用代碼方式創(chuàng)建 schema
如果用程序方式來(lái)定義 Schema凭语,在創(chuàng)建類(lèi)型(type)的時(shí)候葱她,你需要提供
DataFetcher
and TypeResolver
。
如:
DataFetcher<Foo> fooDataFetcher = 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)
.dataFetcher(fooDataFetcher))
.build();
類(lèi)型(Types)
GraphQL 類(lèi)型系統(tǒng)支持以下類(lèi)型
- Scalar
- Object
- Interface
- Union
- InputObject
- Enum
Scalar
graphql-java
支持以下基本數(shù)據(jù)類(lèi)型( Scalars)似扔。
GraphQLString
GraphQLBoolean
GraphQLInt
GraphQLFloat
GraphQLID
GraphQLLong
GraphQLShort
GraphQLByte
GraphQLFloat
GraphQLBigDecimal
GraphQLBigInteger
Object
SDL Example:
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
Interfaces 是抽象的 類(lèi)型( types)定義.
SDL Example:
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 Example:
interface Cat {
name: String;
lives: Int;
}
interface Dog {
name: String;
bonesOwned: int;
}
union Pet = Cat | Dog
Java 例子:
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();
Enum
SDL Example:
enum Color {
RED
GREEN
BLUE
}
Java 例子:
GraphQLEnumType colorEnum = newEnum()
.name("Color")
.description("Supported colors.")
.value("RED")
.value("GREEN")
.value("BLUE")
.build();
ObjectInputType
SDL Example:
input Character {
name: String
}
Java 例子:
GraphQLInputObjectType inputObjectType = newInputObject()
.name("inputObjectType")
.field(newInputObjectField()
.name("field")
.type(GraphQLString))
.build();
類(lèi)型引用 (Type References) (遞歸類(lèi)型recursive types)
GraphQL 支持遞歸類(lèi)型:如 Person(人)
可以包含很多朋友【譯注:當(dāng)然這些也是人類(lèi)型的】
為了方便聲明這種情況吨些, graphql-java
有一個(gè) GraphQLTypeReference
類(lèi)。
在實(shí)際的 Schema 創(chuàng)建時(shí)炒辉,GraphQLTypeReference
會(huì)變?yōu)閷?shí)際的類(lèi)型豪墅。
例如:
GraphQLObjectType person = newObject()
.name("Person")
.field(newFieldDefinition()
.name("friends")
.type(new GraphQLList(new GraphQLTypeReference("Person"))))
.build();
如果用SDL(ID L)來(lái)定義 Schema ,不需要特殊的處理黔寇。
Schema IDL的模塊化
很大的 Schema IDL 文件很難查看偶器。所以我們有兩種方法可以模塊化 Schema。
方法一是合并多個(gè) Schema IDL 文件到一個(gè)邏輯單元( logic
unit)缝裤。下面的例子是屏轰,在 Schema 生成前,合并多個(gè)獨(dú)立的文件憋飞。
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 IDL 還有其它方法去做模塊化霎苗。你可以使用 type extensions
去為現(xiàn)有類(lèi)型增加字段和 interface。
例如榛做,一開(kāi)始唁盏,你有這樣一個(gè)文件:
type Human {
id: ID!
name: String!
}
你的系統(tǒng)的其它模塊可以擴(kuò)展這個(gè)類(lèi)型:
extend type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
你可以按你的需要去擴(kuò)展。它們會(huì)以被發(fā)現(xiàn)的順序組合起來(lái)瘤睹。重復(fù)的字段會(huì)被合并(但重定義一個(gè)字段的類(lèi)型是不允許的)升敲。
extend type Human {
homePlanet: String
}
完成合并后的 Human 類(lèi)型會(huì)是這樣的:
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
homePlanet: String
}
這在頂層查詢(xún)中特別有用答倡。你可以用 extension types 去為頂層 “query”
增加字段每個(gè)團(tuán)隊(duì)可以提供自己的字段集轰传,進(jìn)而合成完整的查詢(xún)。
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(訂閱)的支持
訂閱功能還未在規(guī)范中: graphql-java
現(xiàn)在只支持簡(jiǎn)單的實(shí)現(xiàn) 瘪撇,你可以用
GraphQLSchema.Builder.subscription(...)
在 Schema
中定義訂閱获茬。這使你可以處理訂閱請(qǐng)求。
subscription foo {
# normal graphql query
}