• 记录初学Spring boot中使用GraphQL编写API的几种方式


    Spring boot+graphql

    一、使用graphql-java-tools方式

    <dependency>
        <groupId>com.graphql-java-kickstart</groupId>
        <artifactId>graphql-java-tools</artifactId>
        <version>5.6.0</version>
    </dependency>
    
    <dependency>
        <groupId>com.graphql-java-kickstart</groupId>
        <artifactId>graphiql-spring-boot-starter</artifactId>
        <version>5.0.4</version>
    </dependency>
    

    schema.graphqls

    type Query {
        books: [Book!]
    }
    
    type Book {
        id: Int!
        name: String!
        author: Author!
    }
    
    type Author {
        id: Int!
        name: String!
    }
    

    对应的java class

    class Book {
        private int id;
        private String name;
        private int authorId;
    
        // constructor
    
        // getId
        // getName
        // getAuthorId
    }
    
    class Author {
        private int id;
        private String name;
    
        // constructor
    
        // getId
        // getName
    }
    

    Book-Resolver

    class BookResolver implements GraphQLResolver<Book> {
    
        private AuthorRepository authorRepository;
    
        public BookResolver(AuthorRepository authorRepository) {
            this.authorRepository = authorRepository;
        }
    
        public Author author(Book book) {
            return authorRepository.findById(book.getAuthorId());
        }
    }
    

    Query-Resolver

    class Query implements GraphQLQueryResolver {
    
        private BookRepository bookRepository;
    
        public Query(BookRepository bookRepository) {
            this.bookRepository = bookRepository;
        }
    
        public List<Book> books() {
            return bookRepository.findAll();
        }
    }
    

    Type Query 没有对应的java class,如果type 中的所有字段和java class成员变量一致,则该type可以不用定义Resolver.

    graphql type中的字段映射Java class字段的优先级

    对于graphql objectType中的字段映射为java class字段顺序如下:

    1. method (*fieldArgs [, DataFetchingEnvironment])
    2. method is(*fieldArgs [, DataFetchingEnvironment]), only if the field returns a Boolean
    3. method get(*fieldArgs [, DataFetchingEnvironment])
    4. method getField(*fieldArgs [, DataFetchingEnvironment])
    5. field

    例如:

    上述type Book中的name字段的映射顺序为:

    1. 在java Book类中找name(参数)方法。
    2. 如果Book类中没有name(参数)方法,则继续找isName(参数)方法。
    3. 如果Book中没有isName(参数)方法,则继续在Book中找getName(参数)方法。
    4. 如果Book中没有getName()方法,则继续在Book中找getFieldName(参数)方法。
    5. 如果Book中没有getFieldName(参数)方法,在继续在Book中找name成员变量。
    6. 如果Book中没有name成员变量,则报错。

    graphql type中的字段映射Resolver的优先级:

    1. method (dataClassInstance, *fieldArgs [, DataFetchingEnvironment])
    2. method is(dataClassInstance, *fieldArgs [, DataFetchingEnvironment]), only if the field returns a Boolean
    3. method get(dataClassInstance, *fieldArgs [, DataFetchingEnvironment])
    4. method getField(dataClassInstance, *fieldArgs [, DataFetchingEnvironment])

    注:Resolver的映射优先级高于Java Class,首先在Resolver中查找,如果没找到,才会在Java class中查找 :例如上述type Book中的author字段,会首先映射为BookResolver重的author(Book)方法。

    解析schema.graphqls,创建Graphql对象:

    import com.coxautodev.graphql.tools.SchemaParser;
    
    GraphQLSchema schema = SchemaParser.newParser().file("schema.graphqls")
            .resolvers(new QueryResolver(), new BookResolver())
            .build()
            .makeExecutableSchema();
    
    GraphQL graphQL = GraphQL.newGraphQL(schema).build();
    // 执行查询
    ExecutionResult result = graphQL.execute(query);
    
    Map<String, Object> map = result.toSpecification();
    

    二、不使用Resolver

    schema.graphqls:

    type Query {
      bookById(id: ID): Book 
    }
    
    type Book {
      id: ID
      name: String
      pageCount: Int
      author: Author
    }
    
    type Author {
      id: ID
      firstName: String
      lastName: String
    }
    

    加载schema.graphqls,创建GraphQL对象:

    import graphql.schema.idl.SchemaParser;
    
    @Value("classpath:schema.graphqls")
    Resource resource;
    
    @PostConstruct
    private void loadSchema() throws Exception {
        File schemaFile = resource.getFile();
    
        GraphQLSchema schema = buildSchema(schemaFile);
    
        graphQL = GraphQL.newGraphQL(schema).build();
    }
    
    private GraphQLSchema buildSchema(File file) throws Exception {
        TypeDefinitionRegistry registry = new SchemaParser().parse(file);
        RuntimeWiring runtimeWiring = buildWiring();
        SchemaGenerator schemaGenerator = new SchemaGenerator();
        return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
    }
    
    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
            // 为每个graphql type的字段提供DataFetcher
            .type(newTypeWiring("Query")
                  .dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
            .type(newTypeWiring("Book")
                  .dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
            .build();
    }
    

    DataFetcher:

    @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 getBookByIdDataFetcher() {
            return dataFetchingEnvironment -> {
                String bookId = dataFetchingEnvironment.getArgument("id");
                return books
                        .stream()
                        .filter(book -> book.get("id").equals(bookId))
                        .findFirst()
                        .orElse(null);
            };
        }
    
        public DataFetcher getAuthorDataFetcher() {
            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);
            };
        }
    }
    

    注:这种方式,并不要求一定要提供type对应的java class,只要在对应的DataFetcher中返回符合type的数据格式即可

    三、方式三,不使用graphql-java-tools

    不定义schema.graphqls,以编码的方式创建graphql type。

    定义graphql type:

    GraphQLObjectType fooType = newObject()
            .name("Foo")
            .field(newFieldDefinition()
                    .name("bar")
                    .type(GraphQLString))
            .build();
    

    上述代码相当于使用schema方式创建了如下graphql type:

    type Foo {
    	bar: String
    }
    

    为类型的field指定DataFetcher:

    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();
    

    完整的代码:

    // 定义一个type Query 
    /**
    *  相当于 type QueryType{ hello: String }
    */
    GraphQLObjectType queryType = newObject()
                .name("QueryType")
                .field(newFieldDefinition()
                        .name("hello")
                        .type(GraphQLString)
                        .dataFetcher(new StaticDataFetcher("world!"))
                .build();
    // 创建GraphQLSchema
        GraphQLSchema schema = GraphQLSchema.newSchema()
                .query(queryType)
                .build();
    
        // Make the schema executable
        GraphQL executor = GraphQL.newGraphQL(graphQLSchema).build();
        ExecutionResult executionResult = executor.execute("{hello}");
    

    四、参考链接

    https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/

    https://www.graphql-java-kickstart.com/tools/

    https://www.graphql-java.com/documentation/v11/

  • 相关阅读:
    sqlalchemy 转json 的几种常用方式
    程序员的思维模型指南
    软件的本质
    Python数据模型及Pythonic编程
    Linux Kernel C语言编程范式
    U-Boot内存管理
    Linux网络文件系统的实现与调试
    Linux内核内存管理架构
    Linux多核并行编程关键技术
    Go/Python/Erlang编程语言对比分析及示例
  • 原文地址:https://www.cnblogs.com/yourblog/p/10793534.html
Copyright © 2020-2023  润新知