GraphQL使用之graphql-java自定义标量类型

884 阅读4分钟

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

前言

再谈GraphQL之在spring boot项目中快速应用中,我们写了一个demo工程,业务逻辑以查询项目信息为例。本文会继续以这个demo作为说明进行相关补充,demo地址:GitHub - xxd763795151/graphql.demo: spring boot + graphql快速构建脚手架示例

何谓“标量”

在GrapQL中,标量类型指的是类型系统的叶子节点,在进行查询的时候,不能进行更深层次的查询,表示的是一个不可再分割的值。

类比java语言,可以看作是一个“基本类型”,但这种说法又不太正确,因为GraphQL可以自定义一些复杂对象作为标量类型,这和java又完全不一样。

如下定义,ID和String就是GraphQL的标量类型

# 定义项目字段
type Item {
    id: ID!
    code: String!
    name: String!
}

默认标量类型

GraphQL默认自带的标量类型如下:

  • Int:有符号 32 位整数。
  • Float:有符号双精度浮点值。
  • String:UTF‐8 字符序列。
  • Booleantrue 或者 false
  • ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。

但是这几个基本类型,完全无法满足我们常规的业务开发,比如我现在想在上面的Item里的加上一个createTime字段,是一个Long类型,返回项目创建的时间戳,就不支持:

在idea中直接标红。

自定义标量类型

Schema定义标量类型

# 自定义标量类型:Long
scalar Long

这里定义到了base.graphqls文件中。

java配置已实现的标量类型

graphql-java额外提供了以下几种标量类型,分别对应java的类:

  • Long --->  java.lang.Long
  • Short --->  java.lang.Short
  • Byte --->  java.lang.Byte
  • BigDecimal --->   java.math.BigDecimal
  • BigInteger --->   java.math.BigInteger

在以上5种标量的使用中,我们不需要再额外针对其类型开发序列化、反序列化等相关代码,但是在上面的demo中使用的依赖及初始化方式中还是要定义下相关类型,定义的方式也很简单,如下,增加一行代码:

这样便完成了Long标量类型的定义,其它几种类型方式一样,只需将其中的GraphqlLongCoercing类替换为相对应的即可。

启动工程作一下测试,查询createTime字段,结果预期:

顺便再说明下,demo中的依赖如下,对号入座:

		<dependency>
			<groupId>com.graphql-java-kickstart</groupId>
			<artifactId>graphql-java-tools</artifactId>
			<version>11.0.1</version>
		</dependency>

java定义未实现的标量类型

如果需要定义一些graphql-java未提供的标量类型,需要自已实现该类型的序列化及解析等方法。

现在我给Item再增加一个searchDate字段,是一个Date类型,返回项目搜索时的日期(格式:yyyy-MM-dd HH:mm:ss),因此我们需要新增一个名为Date的标量类型,如下:

# 自定义日期类型,日期格式 yyyy-MM-dd HH:mm:ss
scalar Date

# 定义项目字段
type Item {
    id: ID!
    code: String!
    name: String!
    createTime: Long
    searchDate: Date
}

定义Date标量字段的java代码如下:

public class GraphQLScalarDateType extends GraphQLScalarType {

    private static final String NAME = "Date";

    private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";

    private static final String DESCRIPTION = "Date scalar: " + PATTERN;

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat(PATTERN);

    public GraphQLScalarDateType() {
        this(NAME, DESCRIPTION, new DateCoercing());
    }

    public GraphQLScalarDateType(String name, String description, Coercing coercing) {
        super(name, description, coercing);
    }

    static class DateCoercing implements Coercing<Date, String> {

        // 输出序列化,返回查询结果
        @Override public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
            return DATE_FORMAT.format(dataFetcherResult);
        }

        // 输入,将查询参数转换为运行时的java对象
        @Override public Date parseValue(Object input) throws CoercingParseValueException {
            String dateStr = String.valueOf(input);
            try {
                return DATE_FORMAT.parse(dateStr);
            } catch (ParseException e) {
                throw new CoercingParseValueException("Can not parse: " + dateStr + " as a Date object");
            }
        }

        @Override public Date parseLiteral(Object input) throws CoercingParseLiteralException {
            String dateStr = String.valueOf(input);
            try {
                return DATE_FORMAT.parse(dateStr);
            } catch (ParseException e) {
                throw new CoercingParseValueException("Can not parse: " + dateStr + " as a Date object");
            }
        }
    }
}

最后在构造GraphQL实例的时候配置上该类型即可。

查看效果:

末语

本文示例的完整代码已提交github: GitHub - xxd763795151/graphql.demo: spring boot + graphql快速构建脚手架示例