原文:http://blog.mygraphql.com/wordpress/?p=100java
Schema的主要用途是定義全部可供查詢的字段(field),它們最終組合成一套完整的GraphQL
API.app
「graphql-java」提供兩種方法來定義Schema。用java代碼來定義、用GraphQL
SDL(即IDL)來定義。ide
注意:SDL(IDL)如今還不是 官方 graphql 規範. 本GraphQL實現,是基於
已有的JS參考實現
來開發的。但JS參考實現中的不少代碼也是基於SDL(IDL)語法的,因此你能夠認爲這語法是能夠長期使用的.模塊化
若是你不確認用「java代碼」仍是用「GraphQL
SDL(即IDL)」來定義你的Schema,那麼咱們建議你用SDL(IDL)wordpress
SDL example:fetch
type Foo { bar: String }
java代碼例子:ui
GraphQLObjectType fooType = newObject() .name("Foo") .field(newFieldDefinition() .name("bar") .type(GraphQLString)) .build();
對象 DataFetcher
做用是獲取字段(field)對應的數據;另外,在修改(mutation)操做時,能夠更新數據this
每一個字段都有本身的 DataFetcher
. 若是未爲字段指定DataFetcher,
那麼自動使用默認的 PropertyDataFetcher .scala
PropertyDataFetcher
從 Map
和 Java Beans 中獲取數據.
因此,當Schema中的field名,與Map中的key值,或 Source Object
中的 java
bean 字段名相同時,不須要爲field指定 DataFetcher
.code
對象 TypeResolver
幫助 graphql-java
判斷數據的實際類型(type). 因此Interface
和 Union
均須要指定關聯的 TypeResolver(類型識別器)
.
例如,你有一個 Interface
叫 MagicUserType
它有多是如下的具體類型(Type) Wizard, Witch and Necromancer.
Type resolver(類型識別器) 的做用是在運行時識別出 GraphqlObjectType
的具體類型(Type)。後期具體類型下的field相關的 data
fetcher被調用並獲取數據.
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方法來開發時,你須要同時編寫對應的 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中只是指定了靜態的字段和類型,你還須要把它綁定到java方法中。以讓Schema能夠運行起來
這裏的綁定,包括 DataFetcher
, TypeResolvers
與自定義 Scalar
.
用下頁的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(); }
最後,你能夠經過整合靜態 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);
除了上面的 builder 風格, TypeResolver
s 與 DataFetcher
s 也能夠經過WiringFactory
接口綁定在一塊兒。經過程序去分析 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(); }
若是用程序方式來定義 Schema,在建立類型(type)的時候,你須要提供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();
GraphQL 類型系統支持如下類型
graphql-java
支持如下基本數據類型( Scalars)。
GraphQLString
GraphQLBoolean
GraphQLInt
GraphQLFloat
GraphQLID
GraphQLLong
GraphQLShort
GraphQLByte
GraphQLFloat
GraphQLBigDecimal
GraphQLBigInteger
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();
Interfaces 是抽象的 類型( 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();
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();
SDL Example:
enum Color { RED GREEN BLUE }
Java 例子:
GraphQLEnumType colorEnum = newEnum() .name("Color") .description("Supported colors.") .value("RED") .value("GREEN") .value("BLUE") .build();
SDL Example:
input Character { name: String }
Java 例子:
GraphQLInputObjectType inputObjectType = newInputObject() .name("inputObjectType") .field(newInputObjectField() .name("field") .type(GraphQLString)) .build();
GraphQL 支持遞歸類型:如 Person(人)
能夠包含不少朋友【譯註:固然這些也是人類型的】
爲了方便聲明這種狀況, graphql-java
有一個 GraphQLTypeReference
類。
在實際的 Schema 建立時,GraphQLTypeReference
會變爲實際的類型。
例如:
GraphQLObjectType person = newObject() .name("Person") .field(newFieldDefinition() .name("friends") .type(new GraphQLList(new GraphQLTypeReference("Person")))) .build();
若是用SDL(ID L)來定義 Schema ,不須要特殊的處理。
很大的 Schema IDL 文件很難查看。因此咱們有兩種方法能夠模塊化 Schema。
方法一是合併多個 Schema IDL 文件到一個邏輯單元( logic
unit)。下面的例子是,在 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 IDL 還有其它方法去作模塊化。你能夠使用 type extensions
去爲現有類型增長字段和 interface。
例如,一開始,你有這樣一個文件:
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 }
這在頂層查詢中特別有用。你能夠用 extension types 去爲頂層 「query」
增長字段每一個團隊能夠提供本身的字段集,進而合成完整的查詢。
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 }
訂閱功能還未在規範中: graphql-java
如今只支持簡單的實現 ,你能夠用GraphQLSchema.Builder.subscription(...)
在 Schema
中定義訂閱。這使你能夠處理訂閱請求。
subscription foo { # normal graphql query }