GraphQL简单学习之springboot-3-例子

源码地址:https://github.com/windhan2100/graphql

一、使用例子

1.联合类型

Union可以当成枚举的类型,常用于错误处理

《1.》建立一张表

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` smallint(6) DEFAULT NULL,
  `balance` bigint(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `pwd` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

《2.》添加schema

在schema.graphqls中加入下面内容

######## user 相关 ############
type User {
    id: Long!
    name: String!
    age: Int
    email: String!
    pwd: String!
}
input AuthData {
    pwd: String!
    email: String!
}

union CreateUserResult = CreatedUser | ErrorContainer

type CreatedUser {
    user: User!
}
type ErrorContainer {
    messages:[String!]!
}

在root.graphqls中Mutation中添加

    #### user相关 ####
    createUser(name: String!, authData:AuthData!): CreateUserResult!

《3.》添加类

User.java

package com.hanchao.graphql.graphql.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

/**
 * @author hanliwei
 * @create 2019-03-02 11:03
 */
@Entity
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(columnDefinition = "bigint", nullable = false)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private String pwd;
}

AuthData.java ,Input类型

package com.hanchao.graphql.graphql.model;

import lombok.Data;

/**
 * AuthData.java
 *
 * @author hanliwei
 * @create 2019-03-02 11:16
 */
@Data
public class AuthData {
    private String pwd;
    private String email;
}

CreateUserResult.java 接口类型

package com.hanchao.graphql.graphql.model;

/**
 * union CreateUserResult
 *
 * @author hanliwei
 * @create 2019-03-02 11:13
 */
public interface CreateUserResult {
}

CreatedUser.java 实现了CreateUserReSult接口

package com.hanchao.graphql.graphql.model;

import com.hanchao.graphql.graphql.entity.User;
import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * CreatedUser.java
 *
 * @author hanliwei
 * @create 2019-03-02 11:14
 */
@Data
@AllArgsConstructor
public class CreatedUser implements CreateUserResult {
    private User user;
}

ErrorContainer.java 实现了CreateUserResult接口

package com.hanchao.graphql.graphql.model;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.List;

/**
 * ErrorContainer.java
 *
 * @author hanliwei
 * @create 2019-03-02 11:15
 */
@Data
@AllArgsConstructor
public class ErrorContainer implements CreateUserResult {
    private List<String> messages;
}

UserRepo.java : JPA接口

package com.hanchao.graphql.graphql.repo;

import com.hanchao.graphql.graphql.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author hanliwei
 * @create 2019-03-02 11:18
 */
public interface UserRepo extends JpaRepository<User,Long> {
    /**
     * @descript: 通过用户名查找用户
     * @auther: hanliwei
     * @date: 2019/3/2 11:29
     * @param name  用户名
     * @return User
     */
    User findUserByName(String name);


}

Mutation.java中添加root.graphqls中新加的方法定义

    public CreateUserResult createUser(String name, AuthData authData) {
        if (userRepo.findUserByName(name) != null) {
            return new ErrorContainer(Stream.of("The user already exists.").collect(Collectors.toList()));
        } else {
            User user = new User();
            user.setPwd(authData.getPwd());
            user.setEmail(authData.getEmail());
            user.setName(name);
            return new CreatedUser(userRepo.save(user));
        }
    }

Application.java中添加:不然会抛出下面的异常:

Object type 'CreatedUser' is a member of a known union, but no class could be found for that type name.

Please pass a class for type 'CreatedUser' in the parser's dictionary.

package com.hanchao.graphql.graphql;

import com.coxautodev.graphql.tools.SchemaParserDictionary;
import com.hanchao.graphql.graphql.model.CreatedUser;
import com.hanchao.graphql.graphql.model.ErrorContainer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class GraphqlApplication {

	public static void main(String[] args) {
		SpringApplication.run(GraphqlApplication.class, args);
	}

	@Bean
	SchemaParserDictionary schemaParserDictionary() {
	    return new SchemaParserDictionary()
                .add(CreatedUser.class)
                .add(ErrorContainer.class);
    }

}

《4.》测试一下

第一次请求:

第二次请求:

2.分页

分页我们可以自己定义,也可以使用graphQL提供的Relay(中继) ,下面我们以graphQL的Relay为例子:

参考:https://www.graphql-java-kickstart.com/tools/relay/

《1.》更新Pom依赖

之前的版本有点低,我们需要5.4.0以上

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

《2.》修改schema

在root.graphqls中的Query中添加方法

    #分页#
    books(first: Int, after: String): BookConnection @connection(for: "Book")

在schema.graphqls中添加如下:

#### 分页 ####
type PageInfo {
  hasPreviousPage: Boolean!
  hasNextPage: Boolean!
}
type BookEdge {
    cursor: String
    node: Book
}
type BookConnection {
    edges: [BookEdge]
    pageInfo: PageInfo
}

《3.》Query.java中添加对应的方法

    /**
     * 分页测试
     * @param first
     * @param after
     * @param env
     * @return
     */
    public Connection<Book> books(int first,String after,DataFetchingEnvironment env) {
        return new SimpleListConnection<>(bookRepo.findAll()).get(env);
    }

《4.》测试一下

 

3.接口|内联|外联

跟许多类型系统一样,GraphQL 支持接口。一个接口是一个抽象类型,它包含某些字段,而对象类型必须包含这些字段,才能算实现了这个接口。

例如:我们用Animal接口表示所有的动物接口,所有的动物都会有一个名字,但是,都会有不同的其他的属性,比如:鱼有尾部,狗有四条腿,鸟儿有翅膀

《1.》添加schema

1.在schema.graphqls中添加

### 接口演示 ####
interface Animal {
    name: String!
}

type Fish implements Animal {
    name: String!
    tailColor: String!
}

type Dog implements Animal {
    name: String!
    legs: Int!
}

2.在root.graphqls中添加

    ### 测试接口 ####
    getAnimal(name:String!): Animal!
    animals: [Animal]

《2.》实体类

Animal.java

package com.hanchao.graphql.graphql.model;


/**
 * 为演示GraphQL interface创建的接口
 *
 * @author hanliwei
 * @create 2019-02-20 17:26
 */
public interface Animal {
    String getName();
}

Dog.java

package com.hanchao.graphql.graphql.model;

import lombok.Data;

/**
 * 为演示GraphQL interface创建的实现实体
 *
 * @author hanliwei
 * @create 2019-02-20 17:27
 */
@Data
public class Dog implements Animal {

    private String name;
    private int legs;
}

Fish.java

package com.hanchao.graphql.graphql.model;

import lombok.Data;

/**
 * 为演示GraphQL interface创建的实现实体
 *
 * @author hanliwei
 * @create 2019-02-20 17:29
 */
@Data
public class Fish implements Animal {
    private String tailColor;
    private String name;

}

《3.》查询方法

    /**
     * @descript: 测试一下接口的使用
     * @auther: hanliwei
     * @date: 2019/3/3 18:31
     * @param name
     * @return
     */
    public Animal getAnimal(String name) {
        Dog dog = new Dog();
        dog.setName(name);
        dog.setLegs(4);

        Fish fish = new Fish();
        fish.setName(name);
        fish.setTailColor("BlueAndRead");

        if ("dog".equals(name)) {
            return dog;
        } else if ("fish".equals(name)) {
            return fish;
        }
        return null;
    }

    /**
     * @descript: 返回不同类型的数据
     * @auther: hanliwei
     * @date: 2019/3/3 20:42
     * @return
     */
    public List<Animal> animals() {
        Dog dog = new Dog();
        dog.setName("I am Dog");
        dog.setLegs(4);

        Fish fish = new Fish();
        fish.setName("I am Fish");
        fish.setTailColor("BlueAndRead");

        List<Animal> list = new ArrayList<>();
        list.add(dog);
        list.add(fish);

        return list;
    }

《4.》添加接口类型

这一步很重要,否则会抛异常,具体异常见上面的例子!

package com.hanchao.graphql.graphql;

import com.coxautodev.graphql.tools.SchemaParserDictionary;
import com.hanchao.graphql.graphql.model.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class GraphqlApplication {

	public static void main(String[] args) {
		SpringApplication.run(GraphqlApplication.class, args);
	}

	@Bean
	SchemaParserDictionary schemaParserDictionary() {
	    return new SchemaParserDictionary()
                .add(CreatedUser.class)
                .add(ErrorContainer.class)
				.add(LoginPayload.class)
				.add(Dog.class) //Dog 
				.add(Fish.class); // Fish
    }

}

《5.》测试一下

如果查询的字段返回的是接口或联合类型,那么可能需要返回内联片段来取出下层具体类型的数据:

下面的查询中,因为getAnimal返回的是Animal类型,取决于name参数,其可能是Fish或Dog

在直接选择的情况下,只能请求Animal上存在的字段,比如:name

如果要请求具体类型上的字段,你需要使用一个类型条件内联片段。

因为第一个片段标注为 ... on Fish ,tailColor仅在getAnimal返回的Animal为Fish类型才执行。

同理适用于Dog类型的legs字段。

如果是Dog则查询legs字段,如果是Fish则查询tailColor字段,这种内联如果单个查询则比较方便,但多个查询则使用外联更简洁,如下:

4.字段加参数

《1.》schema

我们给前面的例子中的Book,加入一个字段money ,参数为枚举,不能为空,传入CHINA时,返回:¥:xxx ; 传入USA,返回:$:xxx 。

但是,Book实体类并没有该属性,可见,schema的对象类型,和实际的实体类型,并没有直接关系,属性值也不是一一对应的!!

GraphQL每个字段上都可以有零个或多个参数,所有的参数都是有名字和类型约束的,参数可能是必选或可选的,当一个参数是可选的,我们可以定义一个默认值!

《2.》自定义解析器

《3.》测试一下

注意事项:double和float不能进行精确计算,所以,上面的值为121.1999999999 !!

                 如果进行精确计算,参考:https://blog.51cto.com/hanchaohan/1323228

5.自定义标量类型

大部分的 GraphQL 服务实现中,都有自定义标量类型的方式。例如,我们可以定义一个 Date 类型:

scalar Date

然后就取决于我们的实现中如何定义将其序列化、反序列化和验证。例如,你可以指定 Date 类型应该总是被序列化成整型时间戳,而客户端应该知道去要求任何 date 字段都是这个格式。

《1.》Schema

《2.》实现自定义标量类型

package com.hanchao.graphql.graphql.resolver;

import graphql.language.IntValue;
import graphql.schema.Coercing;
import graphql.schema.GraphQLScalarType;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 自定义标量类型-测试
 *
 * @author hanliwei
 * @create 2019-03-08 22:34
 */
@Component
public class DateScalar extends GraphQLScalarType {
    public DateScalar() {
        super("Date", "Built-in Date as timestamp", new Coercing() {
            @Override
            public Long serialize(Object input) {
                if (input instanceof java.util.Date) {
                    return ((java.util.Date) input).getTime();
                }
                return null;
            }

            @Override
            public java.util.Date parseValue(Object input) {
                if (input instanceof Long) {
                    return new java.util.Date((Long) input);
                }
                return null;
            }

            @Override
            public java.util.Date parseLiteral(Object input) {
                if (input instanceof IntValue) {
                    return new Date(((IntValue) input).getValue().longValue());
                }
                return null;
            }
        });
    }
}

《3.》测试一下

可以看到不同了吗?!Book中时间使用的是自定义类型Date,而Author中是在解析器中重新定义了getCreatedTime方法

自定义标量类型参考:

https://www.graphql-java.com/documentation/v10/scalars/

https://www.apollographql.com/docs/graphql-tools/scalars.html

参考

例子源码:https://github.com/windhan2100/graphql

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值