GraphQL基础知识与Spring for GraphQL使用教程

2023-09-17 23:08:15


GraphQL是一种用于API开发的查询语言和运行时环境。它由Facebook开发并于2015年开源。GraphQL的主要目标是提供一种更高效、灵活和易于使用的方式来获取和操作数据。与传统的RESTful API相比,GraphQL允许客户端精确地指定需要的数据,并减少了不必要的网络传输和数据处理。
采用GraphQL,甚至不需要有任何的接口文档,在定义了Schema之后,服务端实现Schema,客户端可以查看Schema,然后构建出自己需要的查询请求来获得自己需要的数据。

1、数据类型

1.1、标量类型

  1. Int -32位整型数字;
  2. Float-双精度浮点型;
  3. String-UTF‐8 字符序列;
  4. Boolean-布尔型,true 或者 false;
  5. ID-标识类型,唯一标识符,注意此ID为字符,如果使用Mysql自增长id,也会自动转为对应的字符串;

1.2. 高级数据类型

  1. Object - 对象,用于描述层级或者树形数据结构。Object类型有一个类型名,以及类型包含的字段。
type Product {
    id: ID!
    info: String!
    price: Float
}

在此示例中,声明了Product对象类型,定义了3 个字段:
id:非空 ID 类型。
info:非空字符串类型。
price:浮点型。

  1. Interface-接口,用于描述多个类型的通用字;与 Object一样。
interface Product {
    id: ID!
    info: String!
    price: Float
}
  1. Union-联合类型,用于描述某个字段能够支持的所有返回类型以及具体请求真正的返回类型;
  2. Enum-枚举,用于表示可枚举数据结构的类型;
enum Status {
  Yes
  No
}
type Product {
    id: ID!
    info: String!
    price: Float
    stat: Status
}
  1. Input-输入类型input本质上也是一个type类型,是作为Mutation接口的输入参数。换言之,想要定义一个修改接口(add,update,delete)的输入参数对象,就必须定义一个input输入类型。
input BookInput {
    isbn: ID!
    title: String!
    pages: Int
    authorIdCardNo: String
}
  1. List -列表,任何用方括号 ([]) 括起来的类型都会成为 List 类型。
type Product {
    id: ID!
    info: String
    price: Float
    images: [String]
}
  1. Non-Null-不能为 Null,类型后边加!表示非空类型。例如,String 是一个可为空的字符串,而String!是必需的字符串。

基本操作

  • Query(只读操作)
#schema.graphqls定义操作
type Query {
    allBooks: [Book]!
    bookByIsbn(isbn: ID): Book
}

# 接口查询语法
query{
  allBooks {
    title
    author {
      name
      age
    }
  }
}
  • Mutation(可写操作)
#schema.graphqls定义操作
type Mutation {
    createBook(bookInput: BookInput): Book
    createAuthor(authorInput: AuthorInput): Author
}

# mutation{
#   createAuthor(authorInput:{ 
#     idCardNo: "341234567891234567",
#     name:"test1",
#     age:38
#   }
#   ){
#     name 
#     age
#   }
# }
  • Subscription(订阅操作)
type Subscription {
    greetings: String
}

2、Spring for GraphQL实例

GraphQL只是一种架构设计,具体的实现需要各个技术平台自己实现,目前主流的开发语言基本都已经有现成的类库可以使用,GraphQL Java就是Java平台的实现。
GraphQL Java虽然实现了GraphQL,但是只是一个执行GraphQL请求的引擎,用户在使用中需要创建自己的HTTP服务来提供服务。

Spring for GraphQL为基于GraphQL Java构建的Spring应用程序提供支持,来自 GraphQL Java 团队,它的目标是成为所有Spring、GraphQL应用程序的基础。

spring-graphql中定义的核心注解如下:

  • @GraphQlController:申明该类是GraphQL应用中的控制器
  • @QueryMapping:申明该类或方法使用GraphQL的query操作,等同于@SchemaMapping(typeName = “Query”),类似于@GetMapping
  • @Argument:申明该参数是GraphQL应用的入参
  • @MutationMapping:申明该类或方法使用GraphQL的mutation操作,等同于@SchemaMapping(typeName = “Mutation”)
  • @SubscriptionMapping:申明该类或方法使用GraphQL的subscription操作,等同于@SchemaMapping(typeName = “Subscription”)
  • @SchemaMapping:指定GraphQL操作类型的注解,类似@RequestMapping

2.1、项目目录

项目代码目录
在这里插入图片描述

2.2、数据库表

数据表结构,这里例子简单用了用户表和用户日志表

CREATE TABLE `admin_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
    
CREATE TABLE `admin_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `visit_url` varchar(255) DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4

2.3、GraphQL的schema.graphql

GraphQL对应的schema.graphql定义文件,注意GraphQL默认只支持标量类型,DateTime自定义类型使用graphql-java-extended-scalars:https://github.com/graphql-java/graphql-java-extended-scalars提供

scalar DateTime

type AdminUser{
    id: ID!
    name: String!
}

type AdminLog{
    id: ID!
    visitUrl: String
    user: AdminUser!
    createDate: DateTime
}

type Query {
    allLogs:[AdminLog]
    logByUser(userid: ID): [AdminLog]
}

type Mutation {
    createUser(adminUserInput: AdminUserInput): AdminUser
    createLog(adminLogInput: AdminLogInput): AdminLog
}


input AdminLogInput {
    userId: String!
    visitUrl: String
    createDate: DateTime
}

input AdminUserInput {
    name: String!
}

type Subscription {
    greetings: String
}

2.4、Java代码

pom.xml依赖包文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.10</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.penngo.example</groupId>
	<artifactId>graphql-app</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>graphql-app</name>
	<description>graphql-app project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
		<maven.compiler.source>17</maven.compiler.source>
		<maven.compiler.target>17</maven.compiler.target>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-graphql</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.23</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.graphql-java/graphql-java-extended-scalars -->
		<dependency>
			<groupId>com.graphql-java</groupId>
			<artifactId>graphql-java-extended-scalars</artifactId>
			<version>19.1</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<repositories>
		<repository>
			<id>alimaven</id>
			<name>Maven Aliyun Mirror</name>
			<url>https://maven.aliyun.com/repository/central</url>
		</repository>
	</repositories>

	<pluginRepositories>
		<pluginRepository>
			<id>aliyun-plugin</id>
			<url>https://maven.aliyun.com/repository/public</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>

</project>

对应的数据库实体类

package com.penngo.example.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.OffsetDateTime;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminLog {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
    private String userId;
    private String visitUrl;
    private OffsetDateTime createDate;
}

package com.penngo.example.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminUser {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
    private String name;
}

GraphQL对应的输入类

package com.penngo.example.entity;
import lombok.Data;
import java.time.OffsetDateTime;
@Data
public class AdminLogInput {
    private String userId;
    private String visitUrl;
    private OffsetDateTime createDate;

}

package com.penngo.example.entity;
import lombok.Data;
@Data
public class AdminUserInput {
    private String name;
}

对应的数据库操作类

package com.penngo.example.repository;
import com.penngo.example.entity.AdminUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AdminUserRepository  extends JpaRepository<AdminUser,Long> {
    AdminUser findById(String userId);
}

package com.penngo.example.repository;
import com.penngo.example.entity.AdminLog;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface AdminLogRepository  extends JpaRepository<AdminLog,Long> {
    List<AdminLog> findAllByUserId(String userId);
}

对外服务接口类

package com.penngo.example.controller;
import com.penngo.example.entity.*;
import com.penngo.example.repository.AdminLogRepository;
import com.penngo.example.repository.AdminUserRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;
import java.util.List;

@Controller
public class AdminLogController {
    private final AdminUserRepository adminUserRepository;
    private final AdminLogRepository adminLogRepository;

    public AdminLogController(AdminUserRepository adminUserRepository, AdminLogRepository adminLogRepository){
        this.adminLogRepository = adminLogRepository;
        this.adminUserRepository = adminUserRepository;
    }
    @QueryMapping
    public List<AdminLog> allLogs(){
        List<AdminLog> logsList = adminLogRepository.findAll();
        return logsList;
    }
    @QueryMapping
    public List<AdminLog> logByUser(@Argument String userid){
        return adminLogRepository.findAllByUserId(userid);
    }

    @SchemaMapping(typeName = "AdminLog" ,field = "user")
    public AdminUser getAuthor(AdminLog adminLog){
        AdminUser adminUser = adminUserRepository.findById(adminLog.getUserId());
        return adminUser;
    }
    @MutationMapping
    public AdminLog createLog(@Argument AdminLogInput adminLogInput){
        AdminLog log = new AdminLog();
        BeanUtils.copyProperties(adminLogInput,log);
        return adminLogRepository.save(log);
    }
}

package com.penngo.example.controller;
import com.penngo.example.entity.*;
import com.penngo.example.repository.AdminLogRepository;
import com.penngo.example.repository.AdminUserRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import java.util.List;

@Controller
public class AdminUserController {
    private final AdminUserRepository adminUserRepository;
    private final AdminLogRepository adminLogRepository;

    public AdminUserController(AdminUserRepository adminUserRepository, AdminLogRepository adminLogRepository){
        this.adminLogRepository = adminLogRepository;
        this.adminUserRepository = adminUserRepository;
    }
    @QueryMapping
    public List<AdminLog> userById(@Argument String userid){
        return adminLogRepository.findAllByUserId(userid);
    }
    @MutationMapping
    public AdminUser createUser(@Argument AdminUserInput adminUserInput){
        AdminUser user = new AdminUser();
        BeanUtils.copyProperties(adminUserInput,user);
        return adminUserRepository.save(user);
    }
}

package com.penngo.example.controller;
import com.penngo.example.entity.AdminLog;
import com.penngo.example.repository.AdminLogRepository;
import com.penngo.example.repository.AdminUserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
import org.springframework.stereotype.Controller;
import reactor.core.publisher.Flux;
import java.time.Duration;

@Controller
public class GreetingController {
   private final AdminUserRepository adminUserRepository;
   private final AdminLogRepository adminLogRepository;

   public GreetingController(AdminUserRepository adminUserRepository, AdminLogRepository adminLogRepository){
      this.adminLogRepository = adminLogRepository;
      this.adminUserRepository = adminUserRepository;
   }
   // 数据订阅,取最新的5条数据,每5秒发送一条给客户端,一共5次
   @SubscriptionMapping
   public Flux<AdminLog> greetings(){
      System.out.println("greetings====================");
      Page<AdminLog> logsList = adminLogRepository.findAll(PageRequest.of(0,5).withSort(Sort.Direction.DESC, "id"));
      return Flux.fromStream(logsList.stream())
              .delayElements(Duration.ofSeconds(5))
              .take(5);
   }
}

自定义日期数据类型DateTime

package com.penngo.example.component;
import graphql.scalars.ExtendedScalars;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;

@Configuration
public class CustomScalarType {
    @Bean
    public RuntimeWiringConfigurer runtimeWiringConfigurer() {
        return wiringBuilder -> wiringBuilder.scalar(ExtendedScalars.DateTime);
    }
}

服务启动类

package com.penngo.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

3、运行效果

3.1、添加用户

添加用户

mutation{ 
  createUser(adminUserInput: { 
    name: "test1", 
  } ) 
  { 
    id 
    name 
  } }

在这里插入图片描述

3.2、添加日志

添加日志

mutation{
   createLog(adminLogInput: {
      userId: "1",
      visitUrl: "http://localhost:8080/method1"
      createDate: "2023-09-17T19:39:57+08:00"
   } )
   {
      id
      visitUrl
      createDate
   } }  

在这里插入图片描述

3.3、查询所有日志

查询所有日志

query{
  allLogs{
    id
    visitUrl
    user{
    	id
    	name
    }
    createDate
  }
}

在这里插入图片描述

3.4、查询指定用户日志

查询指定用户日志

query{
  logByUser(userid:"1") {
    id
    visitUrl
    user{
    	id
    	name
    }
    createDate
  }
}

在这里插入图片描述

3.5、数据订阅

数据订阅,订阅需要有websocket的支持。

subscription {
  greetings
}

在这里插入图片描述
在这里插入图片描述

4、总结

使用Spring for GraphQL试用了GraphQL后,它实现按需取数据的功能。服务器开发人员和前端开发人员可以通过schema.graphqls定义文件,协定好接口和数据,省掉写接口文档的工作。
缺点可能就是需要一点学习成本,虽然提供数据嵌套可以通过一个请求获取所有数据,但是嵌套复杂可能引起性能问题。

Spring for GraphQL官方参考:https://docs.spring.io/spring-graphql/docs/current/reference/html/#overview

更多推荐

外包干了2个月,技术退步明显.......

先说一下自己的情况,大专生,18年通过校招进入武汉某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试,已经让我变得不思进取,谈了2年的女朋友也因为我的心态和工资和我分手了。于是,我决定要改变现状,冲击下大厂。刚开始准备时

Spring Cloud超越微服务:服务网格的崭露头角

文章目录1.微服务的挑战2.什么是服务网格?3.SpringCloud和服务网格服务发现负载均衡安全性服务网格扩展4.服务网格的优势4.1.解耦通信逻辑4.2.提高可观察性4.3.灰度发布和流量控制4.4.安全性5.未来展望6.结论🎉欢迎来到架构设计专栏~SpringCloud超越微服务:服务网格的崭露头角☆*o(≧

大数据-Spark-Spark开发高频面试题

一、spark的内存分布堆内内存:在这使用堆内内存的时候,如果我们设置了堆内内存2个g的话,读取的数据也是两个g,此时又来两个g的数据,这样就会产生OOM溢出,因为处理完两个g的数据,并不会马上进行GC。堆外内存:这样我们就可以使用堆外内存,也就是物理内存,堆外内存可以精准的申请和释放空间,不需要Gc,性能比较高,提升

基于Hadoop的MapReduce网站日志大数据分析(含预处理MapReduce程序、hdfs、flume、sqoop、hive、mysql、hbase组件、echarts)

需要本项目的可以私信博主!!!本项目包含:PPT,可视化代码,项目源码,配套Hadoop环境(解压可视化),shell脚本,MapReduce代码,文档以及相关说明教程,大数据集!本文介绍了一种基于Hadoop的网站日志大数据分析方法。本项目首先将网站日志上传到HDFS分布式文件系统,然后使用MapReduce进行数据

Microsoft Excel 101 简介

什么是MicrosoftExcel?MicrosoftExcel是一个电子表格程序,用于记录和分析数值数据。将电子表格想像成构成表格的列和行的集合。字母通常分配给列,数字通常分配给行。列和行相交的点称为像元。单元格的地址由代表列的字母和代表行的数字给出。让我们使用下图说明这一点。为什么要学习MicrosoftExcel

IF,AND,OR 或嵌套 IF &在 Excel 中不是逻辑函数

事情并非总是我们希望的那样。意外的事情可能发生。例如,假设您必须将数字相除。尝试将任何数字除以零(0)都会产生错误。在这种情况下,逻辑功能很方便。在本教程中,我们将涵盖以下主题。在本教程中,我们将涵盖以下主题。什么是逻辑功能此功能使我们能够在执行公式和函数时引入决策制定。功能习惯于;检查条件是对还是错结合多个条件Wha

【Linux】线程池 | 自旋锁 | 读写锁

文章目录一、线程池1.线程池模型和应用场景2.单例模式实现线程池(懒汉模式)二、其他常见的锁1.STL、智能指针和线程安全2.其他常见的锁三、读者写者问题1.读者写者模型2.读写锁一、线程池1.线程池模型和应用场景线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等

深度学习之模型压缩、加速模型推理

简介当将一个机器学习模型部署到生产环境中时,通常需要满足一些在模型原型阶段没有考虑到的要求。例如,在生产中使用的模型将不得不处理来自不同用户的大量请求。因此,您将希望进行优化,以获得较低的延迟和/或吞吐量。延迟:是任务完成所需的时间,就像单击链接后加载网页所需的时间。它是开始某项任务和看到结果之间的等待时间。吞吐量:是

Linux内核源码分析 (B.9)深度解读 Linux 内核级通用内存池 —— kmalloc 体系

Linux内核源码分析(B.9)深度解读Linux内核级通用内存池——kmalloc体系文章目录Linux内核源码分析(B.9)深度解读Linux内核级通用内存池——kmalloc体系1\.kmalloc内存池中都有哪些尺寸的内存块2\.kmalloc内存池如何选取合适尺寸的内存块3\.kmalloc内存池的整体架构4

计网第五章(运输层)(六)(TCP可靠传输的实现)

目录一、基本概述二、具体实现1.前后沿:2.利用指针描述发送窗口的状态3.有差错情况之前在数据链路层时已经讨论过可靠传输(计网第三章(数据链路层)(二)(可靠传输)),也在其中提到过可靠传输并不局限于数据链路层。一、基本概述TCP通过以字节为单位的滑动窗口来实现可靠传输。可靠传输的概念在之前已经提到过,这里不再做赘述。

计算机视觉与深度学习-卷积神经网络-纹理表示&卷积神经网络-卷积神经网络-[北邮鲁鹏]

这里写目录标题参考文章全连接神经网络全连接神经网络的瓶颈全连接神经网络应用场景卷积神经网络卷积层(CONV)卷积核卷积操作卷积层设计卷积步长(stride)边界填充特征响应图组尺寸计算激活层池化层(POOL)池化操作定义池化操作作用池化层超参数常见池化操作全连接层(FC)样本增强翻转随机缩放&抠图色彩抖动参考文章计算机

热文推荐