GraphQL SPQR和Spring Boot入门 | baeldung


GraphQL Schema Publisher & Query Resolver,简称 SPQR,是从带注释的 Java 类中生成 GraphQL 模式。
在传统的方法中,如果我们想把GraphQL添加到我们的项目中,我们将不得不遵循两个步骤:

  • 首先,我们必须在项目中添加GraphQL schema数据结构文件。
  • 其次,我们需要编写各自的Java POJO,代表模式中的每个类型。
  •  

这意味着我们要在两个地方维护相同的信息:在模式文件和Java类中。这种方法很容易出错,而且需要花费更多精力来维护项目。
SPQR试图解决这种不匹配mismatch现象:

设置
在Maven中加入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>spqr</artifactId>
    <version>0.11.2</version>
</dependency>

模型类:

public class Book {
    private Integer id;
    private String author;
    private String title;
}

正如我们在上面看到的,它不包括任何SPQR注释。如果我们不拥有源代码,但想从这个库中获益,这可能非常有用。

服务:

@Service
public class BookService implements IBookService {

    Set<Book> books = new HashSet<>();

    public Book getBookWithTitle(String title) {
        return books.stream()
            .filter(book -> book.getTitle()
                .equals(title))
            .findFirst()
            .orElse(null);
    }

    public List<Book> getAllBooks() {
        return books.stream()
            .collect(Collectors.toList());
    }

    public Book addBook(Book book) {
        books.add(book);
        return book;
    }

    public Book updateBook(Book book) {
        books.remove(book);
        books.add(book);
        return book;
    }

    public boolean deleteBook(Book book) {
        return books.remove(book);
    }
}

用graphql-spqr公开服务
剩下的事情就是创建一个解析器,它将暴露GraphQL的改变和查询。为了做到这一点,我们将使用两个重要的SPQR注解--@GraphQLMutation和@GraphQLQuery。

@Service
public class BookResolver {

    @Autowired
    IBookService bookService;

    @GraphQLQuery(name = "getBookWithTitle")
    public Book getBookWithTitle(@GraphQLArgument(name =
"title") String title) {
        return bookService.getBookWithTitle(title);
    }

    @GraphQLQuery(name =
"getAllBooks", description = "Get all books")
    public List<Book> getAllBooks() {
        return bookService.getAllBooks();
    }

    @GraphQLMutation(name =
"addBook")
    public Book addBook(@GraphQLArgument(name =
"newBook") Book book) {
        return bookService.addBook(book);
    }

    @GraphQLMutation(name =
"updateBook")
    public Book updateBook(@GraphQLArgument(name =
"modifiedBook") Book book) {
        return bookService.updateBook(book);
    }

    @GraphQLMutation(name =
"deleteBook")
    public void deleteBook(@GraphQLArgument(name =
"book") Book book) {
        bookService.deleteBook(book);
    }
}

控制器
最后,我们将定义一个Spring @RestController。为了用SPQR暴露服务,我们将配置GraphQLSchema和GraphQL对象。

@RestController
public class GraphqlController {

    private final GraphQL graphQL;

    @Autowired
    public GraphqlController(BookResolver bookResolver) {
        GraphQLSchema schema = new GraphQLSchemaGenerator()
          .withBasePackages("com.baeldung")
          .withOperationsFromSingleton(bookResolver)
          .generate();
        this.graphQL = new GraphQL.Builder(schema)
          .build();
    }

值得注意的是,我们必须将我们的BookResolver注册为一个单例

在我们使用SPQR的过程中,最后一项任务是创建一个/graphql端点。它将作为与我们的服务的单一联系点,并将执行所要求的查询和变化。

@PostMapping(value = "/graphql")
    public Map<String, Object> execute(@RequestBody Map<String, String> request, HttpServletRequest raw)
      throws GraphQLException {
        ExecutionResult result = graphQL.execute(request.get(
"query"));
        return result.getData();
    }
}

测试
我们可以通过检查/graphql端点来检查结果。例如,让我们通过执行以下cURL命令来检索所有的图书记录。

curl -g \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
"query":"{getAllBooks {id author title }}"}' \
  http:
//localhost:8080/graphql


使用GraphQL SPQR Spring Boot启动器
从事SPQR工作的团队已经创建了一个Spring Boot启动器,这使得使用它更加容易。让我们来看看吧

<dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>graphql-spqr-spring-boot-starter</artifactId>
    <version>0.0.6</version>
</dependency>

@Service
@GraphQLApi
public class BookService implements IBookService {

    Set<Book> books = new HashSet<>();

    @GraphQLQuery(name = "getBookWithTitle")
    public Book getBookWithTitle(@GraphQLArgument(name =
"title") String title) {
        return books.stream()
            .filter(book -> book.getTitle()
                .equals(title))
            .findFirst()
            .orElse(null);
    }

    @GraphQLQuery(name =
"getAllBooks", description = "Get all books")
    public List<com.baeldung.sprq.Book> getAllBooks() {
        return books.stream()
            .toList();
    }

    @GraphQLMutation(name =
"addBook")
    public Book addBook(@GraphQLArgument(name =
"newBook") Book book) {
        books.add(book);
        return book;
    }

    @GraphQLMutation(name =
"updateBook")
    public Book updateBook(@GraphQLArgument(name =
"modifiedBook") Book book) {
        books.remove(book);
        books.add(book);
        return book;
    }

    @GraphQLMutation(name =
"deleteBook")
    public boolean deleteBook(@GraphQLArgument(name =
"book") Book book) {
        return books.remove(book);
    }
}

我们不需要GraphqlController类:/graphql端点将被自动添加。