基于java的Graphql学习笔记

最近有需要使用Graphql +SpringBoot 实现一个BFF 服务,因此对Graphql 进行了学习和整合SpringBoot进行使用,简单写个笔记记录下学习心得(差不多是官网的内容,只是笔者是学习java的,所有借用java方便理解graphql概念,在这里简单记录下,整合SpringBoot原理和使用心得有空再整理一篇)

Graphql 简介

官网的定义是:GraphQL 用于API的查询语言,是通过使用为数据定义的类型系统执行查询的服务器端运行时。GraphQL不绑定任何特定的数据库或存储引擎,而是由您现有的代码和数据支持

个人理解(个人学习是基于java 和SpringBoot的基础上进行学习使用的感悟):
Graphql 是一种协议,可以使前端的查询精确到字段级别,不在是http的接口级别,数据由前端决定返回什么数据,使得接口不再传输冗余字段(前端用不到的字段),其他的写不出来了。

个人理解浅薄,也结合了SpringBoot进行整合使用,使用过程中并未真正理解其优势,可能是学习不够深入,如果读者有其他更深的感悟,也请不吝指导一二

Graphql 核心概念

Fields

​ 字段 ,可以是一个字符串类型(String),也可以是一个对象类型(Object)(同java中的字段)

Arguments

​ 每一个字段和嵌套对象都能有自己的一组参数,从而使得 GraphQL 可以替代多次 API 获取请求。甚至你也可以给 标量(scalar)字段传递参数(例如java中提供了获取标量的有参get方法)

Schema 和类型

Type System

​ 每一个 GraphQL 服务都会定义一套类型,用以描述你可能从那个服务查询到的数据。每当查询到来,服务器就会根据 schema 验证并执行查询.

​ GraphQL 查询语言本质就是在选择对象上的字段, 会有一个特殊的对象 “root” 开始,基于该对象上面进行字段选择

Object Types and Fields

type Character {
  name: String!
  appearsIn: [Episode!]!
}

类比java :
class Character {
	@NotNull
    String name;
    @NoEmpty
    List<Episode> appearsIn;
}

Scalar Types

​ 标量类型,(类同Java中的基础数据类型,已经是最小的类型,没有属性),一个对象类型有自己的名字和字段,而某些时候,这些字段必然会解析到具体数据。这就是标量类型的来源:它们表示对应 GraphQL 查询的叶子节点

默认Scalar Types

Int:有符号 32 位整数。

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

String:UTF‐8 字符序列。

Booleantrue 或者 false

ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型(如果定义了类型为ID,在返回的数据中存在从重复ID的数据,则会直接异常)

Enumeration Types

​ 枚举类型是一种特殊的Scalar Type(可以当成Java中枚举类的无属性简化版)

enum FundType {
  CURRENCY
  LOF
}
Lists and Non-Null

Lists 列表,同Java中的集合
Non-Null,用于检查数据入参,或者返回值是否为空

用于返回值上面:

type Character {
  name: String!
  appearsIn: [Episode]!
}

用于定义字段上的参数:

query DroidById($id: ID!) {
  droid(id: $id) {
    name
  }
}
Interfaces

​ 一个接口是一个抽象类型,它包含某些字段,而对象类型必须包含这些字段,才能算实现了这个接口(类似Java中的父类,但是graphql “子类” 必须全部定义“父类中的字段”)

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}
type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  primaryFunction: String
}
Inline Fragments

如果要查询一个只存在于特定对象类型上的字段,你需要使用Inline Fragments,格式:... on Type
(有点像java中的多态,但是java多态为程序执行过程自动处理,而graphql需要通过… on Type 制定实际对象的类型和字段)
如:

  products(productType:null){
    code
    name
    ... on Fund {
      fundType
      manager (code:"test"){
        id
        name
      }
    }
    ... on Portfolio{
      funds{
         code
          name
          fundType
          manager (code:"test"){
            id
            desc
          }
      }
    }
  }
Union Types

Union Types和接口十分相似,区别在于Union的类型之间不一定存在“继承”关系,类型之间的字段无需有共同字段等联系(目前没发现在java中可起到什么比较有意义的作用)

union SearchResult = Manager | Fund
type Query{
    search(type:String): SearchResult # Union Types
}
  search(type:"rf"){
   __typename # 获取Fund类型名,用于在客户端区分不同的数据类型
    ... on Fund{
      code
    	name
    }
  }
Input Types

传递复杂作为新建对象

type Mutation{
    createFund(input: FundInput!): String!
}
input FundInput {
  code: ID!
  name: String!
}
verify

通过使用类型系统,你可以预判一个查询是否有效。这让服务器和客户端可以在无效查询创建时就有效地通知开发者,而不用依赖运行时检查

  1. 通过使用类型系统,你可以预判一个查询是否有效。这让服务器和客户端可以在无效查询创建时就有效地通知开发者,而不用依赖运行时检查
  2. 查询字段的时候,我们只能查询给定类型上的字段
  3. 查询一个字段时,如果其返回值不是标量或者枚举型,那我们就需要指明想要从这个字段中获取的数据
  4. 类似地,如果一个字段是标量,进一步查询它上面的字段也没有意义
execute

一个 GraphQL 查询在被验证后,GraphQL 服务器会将之执行,并返回与请求的结构相对应的结果,该结果通常会是 JSON 的格式

Query And Mutation Type

每一个 GraphQL 服务都有一个 query 类型,可能有一个 mutation 类型。这两个类型和常规对象类型无差,但是它们之所以特殊,是因为它们定义了每一个 GraphQL 查询的入口。因此如果你看到一个像这样的查询:

type Query {
  hero(episode: Episode): Character
  droid(id: ID!): Droid
}
自定义Scalar Types
 scalar Date

Aliases

{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

Fragments

fragment fundsFields on Fund{
 code
    name
    fundType
    manager (code:"test"){
      id
      desc
    }
}
{
  funds {
    ... fundsFields
  }
}

Inline Fragments

如果你查询的字段返回的是接口或者联合类型,那么你可能需要使用Inline Fragments来取出下层具体类型的数据

products(productType:null){
    code
    name
    __typename
    ... on Fund {
      fundType
      manager (code:"test"){
        id
        name
      }
    }
    ... on Portfolio{
      funds{
         code
          name
          fundType
          manager (code:"test"){
            id
            desc
          }
      }
    }
  }

Operation name

操作类型:query*、*mutation 、subscription

操作名称是你的操作的有意义和明确的名称

Variables

一级方法将动态值提取到查询之外,然后作为分离的字典传进去。这些动态值即称为变量

使用变量:

  1. 使用 $variableName 替代查询中的静态值。
  2. 声明 $variableName 为查询接受的变量之一。
  3. variableName: value 通过传输专用(通常是 JSON)的分离的变量字典中

Variable definitions

$episode: Episode

Default variables

$episode: Episode = "JEDI"

Directives

变量动态地改变我们查询的结构,比如详情比普通概要显示更多字段,则可以通过指令动态调整获取的字段、结构

@include(if: Boolean) 仅在参数为 true 时,包含此字段。
@skip(if: Boolean) 如果参数为 true,跳过此字段

服务端实现也可以定义新的指令来添加新的特性。 TODO ?

Mutations

查询字段时,是并行执行,而变更字段时,是线性执行,一个接着一个

Meta fields

__typename,一个元字段,以获得那个位置的对象类型名称,常配合Inline Fragments一起使用

内省

GraphQL 通过内省系统可以知道 GraphQL Schema 它支持哪些查询

query querySchema{
	__schema{
  		types{  
			name
   		}
	}
  Fund:__type(name: "Fund") {
    name
    kind
    fields{
      name
      type{
        name
        kind
      }
    }
  }
   ProductType: __type(name: "ProductType") {
    name
    kind
  }
}

中文官网:https://graphql.cn/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值