GraphQL服务实现

简介

GraphQL是一种用于API或数据的查询语言。

优势

  • 发出一个GraphQL请求就能准确获得想要的数据,不多不少。 - GraphQL查询总是返回可预测的结果。
  • REST API请求多个资源时,GraphQL可以通过一次请求就获取所需的所有数据。
  • GraphQL基于类型和字段的方式进行查询语言定义。

    Maven引入

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java</artifactId>
    <version>15.0</version>
    </dependency>

Schema定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
schema {
query: Query
}

type Query {
shops: [Shop]
expensiveShops: [Shop]
}

type Shop {
id: ID!
name: String!
departments: [Department]
expensiveDepartments: [Department]
}

type Department {
id: ID!
name: String!
products: [Product]
expensiveProducts: [Product]
}

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

GraphQL实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@Component
@Slf4j
public class GraphQLService {

private GraphQL graphQL;
public GraphQLSchema graphQLSchema;

private boolean cache = true;

@Autowired
DataFetchers dataFetchers;

@Bean
public GraphQL graphQL() {
return graphQL;
}

Cache<String, PreparsedDocumentEntry> sdlCache = Caffeine
.newBuilder()
.maximumSize(1_000)
.initialCapacity(100)
.build();

/**
* To avoid the need for re-parse/validate the GraphQL.Builder
* allows an instance of PreparsedDocumentProvider to reuse Document instances.
*/
PreparsedDocumentProvider preparsedCache = (executionInput, function) -> {
Function<String, PreparsedDocumentEntry> mapCompute = key -> function.apply(executionInput);
return sdlCache.get(executionInput.getQuery(), mapCompute);
};

@PostConstruct
public void init() {
graphQLSchema = buildSchema();

GraphQL.Builder builder = GraphQL.newGraphQL(graphQLSchema);

if (cache) {
builder.preparsedDocumentProvider(preparsedCache);
}

this.graphQL = builder.build();
}

private GraphQLSchema buildSchema() {
SchemaParser schemaParser = new SchemaParser();
TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();

Map<String, InputStream> streamMap = ClassFilesUtils.getInputStreams("schema", ".graphqls");

streamMap.forEach((k, v) -> {
try {
String str = CharStreams.toString(new InputStreamReader(v, StandardCharsets.UTF_8));

typeRegistry.merge(schemaParser.parse(str));
} catch (IOException e) {
log.error("error", e);
} finally {
try {
v.close();
} catch (IOException e) {
log.error("error", e);
}
}
});

RuntimeWiring runtimeWiring = buildWiring();

SchemaGenerator schemaGenerator = new SchemaGenerator();

return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}

private RuntimeWiring buildWiring() {
//扩展类型
return RuntimeWiring.newRuntimeWiring()
.scalar(ExtendedScalars.GraphQLByte)
.scalar(ExtendedScalars.GraphQLShort)
.scalar(ExtendedScalars.GraphQLMap)
.type(newTypeWiring("Query").dataFetcher("xxx", dataFetchers.findXXX())).build();
}
}

扩展类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@PublicApi
public class ExtendedScalars {
/**
* This represents the "Long" type which is a representation of java.lang.Long
*/
public static final GraphQLScalarType GraphQLLong = JavaPrimitives.GraphQLLong;

/**
* This represents the "Short" type which is a representation of java.lang.Short
*/
public static final GraphQLScalarType GraphQLShort = JavaPrimitives.GraphQLShort;

/**
* This represents the "Byte" type which is a representation of java.lang.Byte
*/
public static final GraphQLScalarType GraphQLByte = JavaPrimitives.GraphQLByte;

/**
* This represents the "BigDecimal" type which is a representation of java.math.BigDecimal
*/
public static final GraphQLScalarType GraphQLBigDecimal = JavaPrimitives.GraphQLBigDecimal;

/**
* This represents the "BigInteger" type which is a representation of java.math.BigInteger
*/
public static final GraphQLScalarType GraphQLBigInteger = JavaPrimitives.GraphQLBigInteger;

/**
* This represents the "Char" type which is a representation of java.lang.Character
*/
public static final GraphQLScalarType GraphQLChar = JavaPrimitives.GraphQLChar;

public static final GraphQLScalarType GraphQLMap = JavaPrimitives.GraphQLMap;
}

支持Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static final GraphQLScalarType GraphQLMap = new GraphQLScalarType("Map", "A custom map scalar type", new Coercing() {
@Override
public Map serialize(Object dataFetcherResult) throws CoercingSerializeException {
Map map;
try {
map = (Map) dataFetcherResult;
} catch (ClassCastException exception) {
throw new CoercingSerializeException("Could not convert " + dataFetcherResult + " into a Map", exception);
}
return map;
}

@Override
public Map parseValue(Object input) throws CoercingParseValueException {
Map map;
try {
map = (Map) input;
} catch (ClassCastException exception) {
throw new CoercingParseLiteralException("parseValue called");
}
return map;
}

@Override
public Map parseLiteral(Object input) throws CoercingParseLiteralException {
Map map;
try {
map = (Map) input;
} catch (ClassCastException exception) {
throw new CoercingParseLiteralException("parseLiteral called");
}
return map;
}
});

扩展类型定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
scalar Short
scalar Byte
scalar Map

schema {
query: Query
mutation: Mutation
}

type Person {
id: Int
extras: [Map!]
img: Short
type: Byte
}

GraphQL调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Service
@Slf4j
public class GraphQLFetchService {
@Autowired
GraphQL graphQL;

public Map<String, Object> executeGraphQL(ExecutionInput executionInput) {

//执行ql
CompletableFuture<ExecutionResult> resultCompletableFuture = graphQL.executeAsync(executionInput);

Map<String, Object> resultMap = null;
try {
ExecutionResult executionResult = resultCompletableFuture.get();

resultMap = getExecutionResult(executionResult);

} catch (Exception e) {
log.error("error", e);
}

return resultMap;
}

public Map<String, Object> getExecutionResult(ExecutionResult executionResult) {
return executionResult.toSpecification();
}

public CompletableFuture<ExecutionResult> executeGraphQLCompletionStage(ExecutionInput executionInput) {
//执行ql
return graphQL.executeAsync(executionInput);
}
}

DataFetcher定义

1
2
3
4
5
6
7
8
9
10
11
12
public DataFetcher find(ILoader loader, String idName){
return new AsyncDataFetcher(environment -> {
//标识名称
String id = environment.getArgument(idName);
Integer version = environment.getArgument("version");

Map<String, Object> map = new HashMap<>();
map.put(idName, id);

return map;
}, ThreadPool.dataPool);
}
------ 本文结束------

本文标题:GraphQL服务实现

文章作者:Perkins

发布时间:2020年09月01日

原始链接:https://perkins4j2.github.io/posts/522360011/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。