Java高级交流graphql

GraphQL Java从入门到实践

2019-04-21  本文已影响0人  六月星空2011

源码解析

GraphQL Java 从Schema文件到GraphQL实例
GraphQL Java 一次完整的执行历程
补充:GraphQL相关资料

一、GraphQL是什么

GraphQL 是一种协议和一种查询语言。2012年,GraphQL由Facebook内部开发,2015年开源。

二、GraphQL Java入门

GraphQL的服务端在多个语言都有实现包括Haskell, JavaScript, Python, Ruby, Java, C#, Scala, Go, Elixir, Erlang, PHP, R,和 Clojure。
GraphQL Java是GraphQL规范的Java原生实现,也是Java实现的基本核心,所以本文主要讲解GraphQL Java的基本使用以及GraphQL的一些基本概念。如果需要在公司实施GraphQL的话,建议使用针对GraphQL Java封装后的GraphQL Java Tools来实现服务器端的接口改造,因为它比原生实现更简单高效,更加符合面向对象编程思维习惯,当然这是后话了,只要把本文的基本概念弄清楚了,使用它也就是分分钟的事情了。
万丈高楼平地起,接下来还是先从从GraphQL Java实现Hello World开始吧。

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java</artifactId>
     <version>11.0</version>
</dependency>
<!--用于解析schema文件-->
<dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>27.0-jre</version>
</dependency>
public static void main(String[] args) {
// 1\. 定义Schema, 一般会定义在一个schema文件中
String schema = "type Query{hello: String}";
// 2\. 解析Schema
SchemaParser schemaParser = new SchemaParser();
TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema);
// 为Schema 中hello方法绑定获取数据的方法
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
        // 这里绑定的是最简单的静态数据数据获取器, 正常使用时,获取数据的方法返回一个DataFetcher实现即可
        .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world")))
        .build();
// 将TypeDefinitionRegistry与RuntimeWiring结合起来生成GraphQLSchema
SchemaGenerator schemaGenerator = new SchemaGenerator();
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
// 实例化GraphQL, GraphQL为执行GraphQL语言的入口
GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();
// 执行查询
ExecutionResult executionResult = graphQL.execute("{hello}");
// 打印执行结果
System.out.println(executionResult.getData().toString());
}

三、GraphQL Java服务端改造

1. 定义schema

# 定义查询接口, 一个schema文件中只能定义一个Query对象
type Query {
    # 无参, 返回字符串
    hello: String
    # 字段参数且不能为空, 返回普通对象
    bookById(id: ID!): Book
    # 对象参数, 返回列表
    books(book: BookInput): [Book]
    listOrgTrucks(orgTruck: OrgTruckInput):[OrgTruck]
}
# 定义修改接口
type Mutation {
    hello: String
}
# 定义入参对象
input BookInput {
  id: ID
  name: String
}
#定义普通对象
type Book {
  id: ID
  name: String
  pageCount: Int
  author: Author
}
type Author {
  id: ID
  firstName: String
  lastName: String
}

2. 定义DataFetcher

DataFetcher在GraphQL Java服务器中是一个非常重要的概念,在执行查询时,通过Datafetcher获取一个字段的数据。也就是说我们需要为querymutation中定义的方法,以及对定义的对象中的字段绑定一个DataFetcher实现,这样在GraphQL执行语法后才能通过绑定的DataFetcher执行相应的逻辑。也因此GraphQL是一个执行引擎,解析语法后具体要执行什么逻辑,GraphQL并不关心,你只需要在DataFetcher接口并绑定到字段上即可。
当GraphQL Java执行查询时,它为查询中遇到的每个字段调用适当的Datafetcher。DataFetcher是一个只有一个方法的接口,带有一个类型的参数DataFetcherEnvironment:

public interface DataFetcher<T> {
    T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
}
@Component
public class GraphQLDataFetchers {
    private static List<Map<String, String>> books = Arrays.asList(
            ImmutableMap.of("id", "book-1",
                    "name", "Harry Potter and the Philosopher's Stone",
                    "pageCount", "223",
                    "authorId", "author-1"),
            ImmutableMap.of("id", "book-2",
                    "name", "Moby Dick",
                    "pageCount", "635",
                    "authorId", "author-2"),
            ImmutableMap.of("id", "book-3",
                    "name", "Interview with the vampire",
                    "pageCount", "371",
                    "authorId", "author-3")
    );
    private static List<Map<String, String>> authors = Arrays.asList(
            ImmutableMap.of("id", "author-1",
                    "firstName", "Joanne",
                    "lastName", "Rowling"),
            ImmutableMap.of("id", "author-2",
                    "firstName", "Herman",
                    "lastName", "Melville"),
            ImmutableMap.of("id", "author-3",
                    "firstName", "Anne",
                    "lastName", "Rice")
    );
   public DataFetcher getAllBooks() {
        return environment -> {
            Map<String, Object> arguments = environment.getArgument("book");
            Book book = JSON.parseObject(JSON.toJSONString(arguments), Book.class);
            return books;
        };
    }
    public DataFetcher getBookByIdDataFetcher() {
       // dataFetchingEnvironment 封装了查询中带有的参数
        return dataFetchingEnvironment -> {
            String bookId = dataFetchingEnvironment.getArgument("id");
            return books
                    .stream()
                    .filter(book -> book.get("id").equals(bookId))
                    .findFirst()
                    .orElse(null);
        };
    }
    public DataFetcher getAuthorDataFetcher() {
    // 这里因为是通过Book查询Author数据的子查询,所以dataFetchingEnvironment.getSource()中封装了Book对象的全部信息
   //即GraphQL中每个字段的Datafetcher都是以自顶向下的方式调用的,父字段的结果是子Datafetcherenvironment的source属性。
        return dataFetchingEnvironment -> {
            Map<String,String> book = dataFetchingEnvironment.getSource();
            String authorId = book.get("authorId");
            return authors
                    .stream()
                    .filter(author -> author.get("id").equals(authorId))
                    .findFirst()
                    .orElse(null);
        };
    }
}

3.解析Schema并绑定DataFetcher

@Component
public class GraphQLProvider {
    @Autowired
    GraphQLDataFetchers graphQLDataFetchers;
    private GraphQL graphQL;
    @PostConstruct
    public void init() throws IOException {
        URL url = Resources.getResource("schema.graphqls");
        String sdl = Resources.toString(url, Charsets.UTF_8);
        GraphQLSchema graphQLSchema = buildSchema(sdl);
        this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
    }
    private GraphQLSchema buildSchema(String sdl) {
        TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
        RuntimeWiring runtimeWiring = buildWiring();
        SchemaGenerator schemaGenerator = new SchemaGenerator();
        return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
    }
    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                // 仅仅是体验Mutation这个功能,返回一个字符串
                .type("Mutation", builder -> builder.dataFetcher("hello", new StaticDataFetcher("Mutation hello world")))
                // 返回字符串
                .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("Query hello world")))
                // 通过id查询book
                .type(newTypeWiring("Query").dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
                // 查询所有的book
                .type(newTypeWiring("Query").dataFetcher("books", graphQLDataFetchers.getAllBooks()))
                // 查询book中的author信息
                .type(newTypeWiring("Book").dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
                .build();
    }

  // 执行GraphQL语言的入口
    @Bean
    public GraphQL graphQL() {
        return graphQL;
    }

4. 定义controller通过GraphQL查询数据

@RestController
public class GraphQLController {
    @Autowired
    private GraphQL graphQL;
    @RequestMapping(value = "/graphql")
  // 这里定义的一个字符串接口所有的参数,定义对象也是可以的
    public Map<String, Object> graphql(@RequestBody String request) {
        JSONObject req = JSON.parseObject(request);
        ExecutionInput executionInput = ExecutionInput.newExecutionInput()
               // 需要执行的查询语言
                .query(req.getString("query"))
              // 执行操作的名称,默认为null
                .operationName(req.getString("operationName"))
              // 获取query语句中定义的变量的值
                .variables(req.getJSONObject("variables"))
                .build();
       // 执行并返回结果
        return this.graphQL.execute(executionInput).toSpecification();
    }
}

5. 演示上诉代码并说明一些概念

本文使用GraphQLPlayground演示,下载地址:https://github.com/prisma/graphql-playground/releases
当然用官方的graphiql:https://github.com/graphql/graphiql, 或者postman也都是可以的。

6. 此GraphQL Java(原生实现)服务器端搭建存在的问题

参考

扩展阅读

上一篇下一篇

猜你喜欢

热点阅读