打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Spring Boot GraphQL 实战 02_增删改查和自定义标量

hello,大叫好,我是小黑,又和大家见面啦~

今天我们来继续学习 Spring Boot GraphQL 实战,我们使用的框架是 https://github.com/graphql-java-kickstart/graphql-spring-boot

项目 github 地址:https://github.com/shenjianeng/graphql-spring-boot-example

Query(查询)

带参数的查询

首先,在 classpath 下创建 graphqls 文件:

type Book{    id:ID!    name:String!}type Query{    # 根据 id 查询 book,参数名为 id,参数类型的 ID 类型,结果返回 book    getBookById(id:ID!):Book}

创建一个 Spring Bean,此处需要实现 GraphQLQueryResolver 接口,并在该类中自定义一个方法来映射 graphqls 文件中的查询。

@Datapublic class Book {    private int id;    private String name;}@Componentpublic class BookGraphQLQueryResolver implements GraphQLQueryResolver {    public Book getBookById(int id) {        Book book = new Book();        book.setId(id);        book.setName("这边书没有书名");        return book;    }}

复合字段查询

需求:每本书都有作者,在查询书本信息时,有时需要返回作者信息。

# 定义 Author 数据类型结构type Author{    id:ID!    name:String!}type Book{    id:ID!    name:String!    # 增加 author 字段,数据类型为 Author    author:Author}type Query{    # 根据 id 查询 book,参数名为 id,参数类型的 ID 类型,结果返回 book    getBookById(id:ID!):Book}

再看一下此时我们的 Java Bean:

@Datapublic class Author {    private UUID id;    private String name;}@Datapublic class Book {    private long id;    private String name;}

看仔细哦,Book 类中并没有 author 字段,Book 中 author 信息将由 graphql.kickstart.tools.GraphQLResolver 来提供。

@Slf4j@Componentpublic class BookGraphQLResolver implements GraphQLResolver<Book> {    public Author author(Book book) {        log.info("book id :{} query author info", book.getId());        Author author = new Author();        author.setId(UUID.randomUUID());        author.setName(String.format("我是[%s]的作者", book.getName()));        return author;    }}

ok,让我们启动服务,访问 http://localhost:8080/graphiql

而当客户端不需要 author 信息时,服务端就不会执行 BookGraphQLResolver#author,真正做到了使得客户端能够准确地获得它需要的数据,而且没有任何冗余

(ps:如果你是服务端开发,你会怎么实现呢?是给客户端提供一个接口返回 book 和 author 信息,还是给客户端提供两个不同的接口呢?)

Mutation(变更)

在 graphqls 文件中,使用 Query 来定义查询接口,使用 Mutation 可以定义变更数据的操作。

type Mutation{    createBook(id:ID!,name:String!):Book}

上述 graphqls 文件中定义了一个 createBook 的方法,参数列表为 idname ,方法返回创建的 Book 对象。

与之对应的 Java 代码如下:

@Componentpublic class BookGraphQLMutationResolver implements GraphQLMutationResolver {    public Book createBook(int id, String name) {        Book book = new Book();        book.setId(id);        book.setName(name);        return book;    }}

BookGraphQLMutationResolver 实现了 graphql.kickstart.tools.GraphQLMutationResolver 接口,表明当前类中的方法用来映射 graphqls 文件中的 Mutation。

Input Types

当 Mutation 中请求参数特别多时,我们可以使用 Input Types 来优化代码。

type Mutation{    createBook(id:ID!,name:String!):Book    create(bookInput:BookInput!):Book}input BookInput{    id:ID!    name:String!}

同理,我们也需求在 BookGraphQLMutationResolver 中添加对应的方法来映射。

@Componentpublic class BookGraphQLMutationResolver implements GraphQLMutationResolver {  // ...省略其他代码      public Book create(BookInput input) {        Book book = new Book();        book.setId(input.getId());        book.setName(input.getName());        return book;    }}

客户端请求代码如下:

自定义标量类型

在 GraphQL 中自带一些默认标量类型:

  • Int:有符号 32 位整数

  • Float:有符号双精度浮点值

  • String:UTF‐8 字符序列

  • Booleantrue 或者 false

  • ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化

使用 graphql-java-extended-scalars 库

在 Java 这个生态中,我们可以引入下面这个库来帮助我们很方便的进行扩展:

https://github.com/graphql-java/graphql-java-extended-scalars

  <dependency>    <groupId>com.graphql-java</groupId>    <artifactId>graphql-java-extended-scalars</artifactId>    <version>15.0.0</version>  </dependency>

graphql-java-extended-scalars 中具体扩展了哪些标量类型,我们都可以在 graphql.scalars.ExtendedScalars 类中找到。

(ps:一个小技巧,s 结尾的类一般都是工具类)

如何使用呢?

  1. 向 Spring 容器中注册自定义标量

  2. 在 graphqls 文件中声明要使用的自定义标量

  3. 直接使用即可

相关示例代码如下:

@Configurationpublic class CustomScalarTypeConfig {    @Bean    public GraphQLScalarType graphQLLong() {        return ExtendedScalars.GraphQLLong;    }}
scalar Longtype Book{    id:ID!    name:String!    # 增加 author 字段,数据类型为 Author    author:Author    totalPageSize:Long}

使用 GraphQLScalarType 自定义标量类型

我们可以参考 graphql.scalars.java.JavaPrimitives#GraphQLLong 的实现来自定标量类型。

@Beanpublic GraphQLScalarType graphQLDate() {    return GraphQLScalarType            .newScalar()            .name("Date")            .description("Date 类型")            .coercing(new Coercing<Date, String>() {                @Override                public String serialize(Object dataFetcherResult) throws CoercingSerializeException {                    return new SimpleDateFormat(DATE_FORMAT_PATTERN_DEFAULT).format((Date) dataFetcherResult);                }                @Override                public Date parseValue(Object input) throws CoercingParseValueException {                    if (input instanceof String) {                        try {                            return new SimpleDateFormat(DATE_FORMAT_PATTERN_DEFAULT).parse((String) input);                        } catch (ParseException e) {                            throw new CoercingParseValueException(e);                        }                    }                    throw new CoercingParseValueException(                            "Expected a 'String' but was '" + Kit.typeName(input) + "'."                    );                }                @Override                public Date parseLiteral(Object input) throws CoercingParseLiteralException {                    if (!(input instanceof StringValue)) {                        throw new CoercingParseLiteralException(                                "Expected AST type 'StringValue' but was '" + typeName(input) + "'."                        );                    }                    try {                        return new SimpleDateFormat(DATE_FORMAT_PATTERN_DEFAULT).parse(((StringValue) input).getValue());                    } catch (ParseException e) {                        throw new CoercingParseValueException(e);                    }                }            })            .build();}

DataFetcherResult

在 Resolver 中,我们可以使用 graphql.execution.DataFetcherResult 来包装返回的结果,示例代码如下:

@Componentpublic class BookGraphQLQueryResolver implements GraphQLQueryResolver {    public DataFetcherResult<Book> getBookById(int id) {        if (id <= 0) {            return DataFetcherResult                    .<Book>newResult()                    .error(new GenericGraphQLError("id 不能为负数"))                    .build();        }        Book book = new Book();        book.setId(id);        book.setName("这边书没有书名");        return DataFetcherResult                .<Book>newResult()                .data(book)                .build();    }}
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
GraphQL 实战篇之前端Vue 后端
MySQL+SERVLET+JAVABEAN+JSP图书管理系统1
XML数据岛中数据的编辑和添加
Django使用ORM之多对多(四)
Xpath关键字ends-with无法使用的问题
Spring Data JDBC、引用和聚合
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服