GraphQL入门基础篇教程
API 的查询语言。和RESTful核心差异资源的描述信息与其获取方式相分离。RESTful服务端决定返回结果,GraphQL客户端决定返回结果。RESTful和GraphQL都是返回json。
历史
GraphQL是由Facebook创造的。
当时,Facebook想在移动端实现新闻推送,这不像检索一个故事、作者、故事的内容、评论列表和喜欢该文章的人这么简单,而是每个故事都相互关联、嵌套和递归的。现有的API没有被设计成允许开发人员在移动设备上展示一个丰富、类似新闻推送的体验。它们没有层次性,允许开发人员选择他们所需要的,或有显示异构推送故事列表的能力。因此,2012年Facebook决定自己构建一个新的新闻推送API,这就是GraphQL形成时间。同年 8 月中旬,Facebook 发布了采用新 GraphQL 技术的 iOS5.0 应用程序。它允许开发人员通过利用其数据获取(data-fetching)功能来减少网络的使用。在接下来的一年半时间里,除了新闻推送外,GraphQL API 扩展到大多数的 FacebookiOS 应用程序。在 2015 年,GraphQL 规范首次与 JavaScript 中的引用实现一起发布。
参考网址:
GraphQL 的前世今生 - .NET西安社区 - 博客园
是什么?
官网地址:GraphQL | A query language for your API
官网解释:
一种用于 API 的查询语言。
GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。
通俗解释:
和常规sql查询语言不同,它是一种用于前后端数据查询方式的规范。本质就是API查询语言,开发人员可以自定义查询规则,定义自己所需的数据格式,并且在一个请求中获取所有想要的数据(PS:RESTful可能需要多次请求不同接口)。意思就是定义什么返回什么,开发人员对于返回的结果是可预测的。
GraphQL和RESTful区别
参考网址:GraphQL和RESTful的区别 - 奔跑的瓜牛 - 博客园
区别说明:
GraphQL与RESTful都是基于HTTP进行数据的请求与接收。
核心差异:资源的描述信息与其获取方式相分离。
例如根据图书id获取图书信息:
RESTful获取:
GET /books/1
{
"title": "Black Hole Blues",
"author": {
"firstName": "Janna",
"lastName": "Levin"
}
// ... more fields here
}
GraphQL获取:
#1.首先定义数据类型
type Book {
id: ID
title: String
published: Date
price: String
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
books: [Book]
}
#2.创建query
type Query {
book(id: ID!): Book
author(id: ID!): Author
}
#3.http查询
GET /graphql?query={ book(id: "1") { title, author { firstName } } }
{
"title": "Black Hole Blues",
"author": {
"firstName": "Janna",
}
}
二者对比:
相同点:
- 都有资源这个概念,而且都能通过ID去获取资源
- 都可以通过HTTP GET方式来获取资源
- 都可以使用JSON作为响应格式
差异点:
- 在RESTful中,你所访问的路径就是该资源的唯一标识(ID);在GraphQL中,该标识与访问方式并不相关(PS:RESTful url是唯一的,GraphQL一般访问地址是一个,查询query不同)
- 在RESTful中,资源的返回结构与返回数量是由服务端决定;在GraphQL,服务端只负责定义哪些资源是可用的,由客户端自己决定需要得到什么资源。
举个使用场景说明两者差异:
为了更好的理解两者的差异,我们用一个场景来说明。比如现在有一个简单的示例场景:在blog应用程序中,应用程序需要显示特定用户的文章的标题。同一屏幕还显示该用户最后3个关注者的名称。REST和GraphQL如何解决这种情况?
RESTful实现方式:
- 通过/user/<id>获取初始用户数据
- 通过/user/<id>/posts 返回用户的所有帖子
- 请求/user/<id>/followers,返回每个用户的关注者列表
现在有个要求:要获取某个用户信息和用户所有的帖子以及用户关注者信息。
GraphQL实现方式:
结论:RESTful 请求了3次达到目的,并且接口返回了很多并不需要的数据。GraphQL只请求了一次,并且返回的结果是必需的。
有什么用?
官网解释:
- 请求你所要的数据不多不少。
- 获取多个资源只用一个请求。
- 描述所有的可能类型系统。
- API 演进无需划分版本。
谁在用?
怎么用?
了解GrapQL规范
字段(Fields)
在GraphQL的查询中,请求结构中包含了所预期结果的结构,这个就是字段。并且响应的结构和请求结构基本一致,这是GraphQL的一个特性,这样就可以让请求发起者很清楚的知道自己想要什么。
参数(Arguments)
在查询数据时,离不开传递参数,在GraphQL的查询中,也是可以传递参数的,语法∶(参数名:参数值)
别名(Aliases)
如果一次查询多个相同对象,但是值不同,这个时候就需要起别名了,否则json的语法就不能通过了。
这个时候用到别名,使用方法如下:
片段(Fragments)
片段使你能够组织一组字段,然后在需要它们的地方引入。下面例子展示了如何使用片段解决上述场景:
Schema 和类型
Schema是用于定义数据结构的,比如说,User对象中有哪些属性,对象与对象之间是什么关系等。
Schema定义结构:
scalar Long
schema {#定义查询
query:UserQuery
}
"用户查询" #注释
type UserQuery{#定义查询类型
"根据id查询用户"
getUser(userId:Long):UserVO #指定对象以及参数类型
}
"用户返回对象"
type UserVO{ #定义对象
"用户id"
userId:Long! #!表示属性是非空项
userName:String
age:Int
dept:DeptVO
}
type DeptVO{
deptId:Long
deptName:String
}
PS:通过双引号的方式来添加注释。
标量类型(Scalar Types)
GraphQL 自带一组默认标量类型:
- Int:有符号 32 位整数。
- Float:有符号双精度浮点值。
- String:UTF‐8 字符序列。
- Boolean:true 或者 false。
- ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。
大部分的 GraphQL 服务实现中,都有自定义标量类型的方式。例如,我们可以定义一个 Date 类型:
scalar Date
然后就取决于我们的实现中如何定义将其序列化、反序列化和验证。例如,你可以指定 Date 类型应该总是被序列化成整型时间戳,而客户端应该知道去要求任何 date 字段都是这个格式。
枚举类型(Enumeration Types)
也称作枚举(enum),枚举类型是一种特殊的标量,它限制在一个特殊的可选值集合内。这让你能够:验证这个类型的任何参数是可选值的某一个与类型系统沟通,一个字段总是一个有限值集合的其中一个值。
下面是一个用 GraphQL schema 语言表示的 enum 定义:
接口(Interfaces)
跟许多类型系统一样,GraphQL 支持接口。一个接口是一个抽象类型,它包含某些字段,而对象类型必须包含这些字段,才能算实现了这个接口。
例如,你可以用一个 Character 接口用以表示《星球大战》三部曲中的任何角色:
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
这意味着任何实现 Character 的类型都要具有这些字段,并有对应参数和返回类型。
例如,这里有一些可能实现了 Character 的类型:
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}
可见这两个类型都具备 Character 接口的所有字段,但也引入了其他的字段 totalCredits、starships 和 primaryFunction,这都属于特定的类型的角色。
创建第一个GraphQL java项目
根据官网提供的graphql-java来实现的,在实际开发中不推荐使用这种方式,推荐使用graphql-java-kickstart来实现。
打开idea新建一个springboot项目
File->New->Project...
注意:jdk版本是1.8以上,graphql-java要求jdk至少是8。
引入graphql-java maven依赖
可参照graphql-java官网:Getting started | GraphQL Java
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>16.2</version>
</dependency>
引入lombok,注解生成getset
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
编写第一个demo程序
定义查询schame结构
scalar Long
schema {#定义查询
query:UserQuery
mutation: userMutations
}
"用户变更"
type userMutations{
"添加用户"
addUser(addUser:UserSaveDTO):ResultModel
}
"新增用户请求对象"
input UserSaveDTO{#定义新增对象
"用户名称"
userName:String!
"用户年龄"
age:Int!
}
type ResultModel{#定义返回对象
code:Int
msg:String
ecxp:String
}
type UserQuery{#定义查询类型
getUser(userId:Long):UserVO #指定对象以及参数类型
}
type UserVO{ #定义对象
userId:Long! #!表示属性是非空项
userName:String
age:Int
dept:DeptVO
}
type DeptVO{
deptId:Long
deptName:String
}
PS:定义的对象和实体类字段必须保持一样,否则在获取参数或者转换可能报错。
实现根据用户id查询用户信息:
package com.fu.demo;
import com.fu.domain.vo.UserVO;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.Scalars;
import graphql.schema.*;
/**
* @description:
* @create: 2021-11-29 10:06
**/
public class GraphQLDemo1 {
public static void main(String[] args) {
/* schema {
query:UserQuery
}
type UserQuery{
user:User
}
type User{
id:Long!
name:String
age:Integer
}
*/
/**
* type User{#定义对象}
*/
GraphQLObjectType userObjectType = GraphQLObjectType.newObject()
.name("User")
.field(GraphQLFieldDefinition.newFieldDefinition().name("userId").type(Scalars.GraphQLLong))
.field(GraphQLFieldDefinition.newFieldDefinition().name("userName").type(Scalars.GraphQLString))
.field(GraphQLFieldDefinition.newFieldDefinition().name("age").type(Scalars.GraphQLInt))
.build();
/**
* user:User #指定对象及查询类型
*/
GraphQLFieldDefinition userFileldDefinition = GraphQLFieldDefinition.newFieldDefinition()
.name("getUser")
.type(userObjectType)
.argument(GraphQLArgument.newArgument().name("userId").type(Scalars.GraphQLLong).build())
//.dataFetcher(new StaticDataFetcher( new UserVO(1L,"张双",20)))
.dataFetcher(dataFetchingEnvironment -> {
Long id = dataFetchingEnvironment.getArgument("userId");
return new UserVO(id, "张双" + id, 20);
})
.build();
/**
* type UserQuery{#定义查询类型}
*/
GraphQLObjectType userQuery = GraphQLObjectType.newObject()
.name("UserQuery")
.field(userFileldDefinition)
.build();
/**
* schema{#定义查询}
*/
GraphQLSchema graphQLSchema = GraphQLSchema.newSchema().query(userQuery).build();
GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();
String query = "{getUser(userId:100){userId,userName,age}}";
ExecutionResult execute = graphQL.execute(query);
System.out.println(execute.toSpecification());
}
}
执行结果:
根据用户ID查询用户的graphQL查询步骤分析:
实现新增用户:
package com.fu.demo;
import com.fu.common.domain.ResultModel;
import com.fu.domain.vo.UserVO;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @description:
* @author: wangyan
* @create: 2021-11-30 13:52
**/
public class GraphQLDemo3 {
public static void main(String[] args) throws IOException {
String fileName = "graphqls/user.graphql";
String fileContent = IOUtils.toString(GraphQLDemo2.class.getClassLoader().getResource(fileName), "utf-8");
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(fileContent);
RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring().type("userMutations", builder ->
builder.dataFetcher("addUser", dataFetchingEnvironment -> {
Map<String, Object> arguments = dataFetchingEnvironment.getArguments();
System.out.println("新增参数:" + arguments);
return new ResultModel(200, "新增成功", "");
})
).build();
GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();
//定义新增参数
Map<String, Object> param = new HashMap<>();
Map userInfo = new HashMap();
userInfo.put("userName", "王小二");
userInfo.put("age", 3);
param.put("saveParam", userInfo);
//定义graphql输入参数
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.variables(param)
.query("mutation addUser($saveParam:UserSaveDTO){addUser(addUser:$saveParam){code,msg}}")
.build();
ExecutionResult execute = graphQL.execute(executionInput);
System.out.println(execute.toSpecification());
}
}
执行结果:
优化项目中GraphQL查询
SDL查询
在“resources”目录下新建一个“graphqls”文件夹,在里面新建一个“user.graphql”文件。
(PS:idea可以安装JS Graphql插件,安装后编写文件有提示)
PS:定义的字段类型不是GraphQL标量类型需要通过“scalar”定义声明。
代码实现:
package com.fu.demo;
import com.fu.domain.vo.UserVO;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
/**
* @description:
* @create: 2021-11-29 10:06
**/
public class GraphQLDemo2 {
public static void main(String[] args) throws IOException {
String fileName = "graphqls/user.graphql";
String fileContent = IOUtils.toString(GraphQLDemo2.class.getClassLoader().getResource(fileName), "utf-8");
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(fileContent);
RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring().type("UserQuery", builder ->
builder.dataFetcher("getUser", dataFetchingEnvironment -> {
Long id = dataFetchingEnvironment.getArgument("userId");
return new UserVO(id, "张珊" + id, 20);
})
).build();
GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();
String query = "{getUser(userId:100){userId,userName,age}}";
ExecutionResult execute = graphQL.execute(query);
System.out.println(execute.toSpecification());
}
}
到这里graphql算入门了,从上面的demo中可以发现还可以进一步优化,下面进一步优化,通过http访问一个接口传参进行graphql查询。
http接口查询
新建GraphQLProvider类:
目的:获取GraphQL实例
package com.fu.graphql;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
/**
* @description:
* @create: 2021-11-29 13:28
**/
@Component
public class GraphQLProvider {
private GraphQL graphQL;
@Autowired
private List<MyDataFetcher> myDataFetcherList;
@PostConstruct
public void init() throws FileNotFoundException {
File file = ResourceUtils.getFile("classpath:graphqls/user.graphql");
this.graphQL = GraphQL.newGraphQL(buidlGraphQLSchema(file)).build();
}
@Bean
public GraphQL graphQL() {
return graphQL;
}
private GraphQLSchema buidlGraphQLSchema(File file) {
TypeDefinitionRegistry registry = new SchemaParser().parse(file);
return new SchemaGenerator().makeExecutableSchema(registry, buidRuntimeWiring());
}
private RuntimeWiring buidRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring().type("UserQuery", builder -> {
for (MyDataFetcher myDataFetcher : myDataFetcherList) {
builder.dataFetcher(myDataFetcher.fieldName(), dataFetchingEnvironment -> myDataFetcher.dataFetcher(dataFetchingEnvironment));
}
return builder;
}
).build();
}
}
新建MyDataFetcher接口:
目的:每个API接口获取方式都不一样,因此自定义一个获取数据接口。
package com.fu.graphql;
import graphql.schema.DataFetchingEnvironment;
/**
* @description: 获取数据
* @create: 2021-11-29 13:17
**/
public interface MyDataFetcher {
/**graphQL查询的名称*/
String fieldName();
/**接口数据查询*/
Object dataFetcher(DataFetchingEnvironment environment);
}
新建UserController:
package com.fu.domain.controller;
import graphql.ExecutionResult;
import graphql.GraphQL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* @description:
* @create: 2021-11-29 15:57
**/
@RestController
@RequestMapping
public class UserController {
@Autowired
private GraphQL graphQL;
@GetMapping("/graphql")
public Map<String, Object> graphql(String query) {
ExecutionResult execute = graphQL.execute(query);
System.out.println(execute.toSpecification());
return execute.toSpecification();
}
}
项目启动测试
安装google插件:
PS:可以安装谷歌“altair graphql client”插件,安装后,通过这个工具写sdl时会有检验和提示。
实例-查询用户id=1的用户:
实例-查询用户id=1,2的用户:
PS:这里需要用到别名,返回对象一样的情况下,需要用别名,否则报错
实例-多接口合并查询:查询用户id=1,2的用户和用户id=1的角色信息(listRole是一个接口)
(推荐)graphql-java-kickstart实现graphQL
项目基架:springboot2.6
官网地址:
About GraphQL Spring Boot - GraphQL Java Kickstart
与RESTful实现差异对比
从图可以看出,接收前端请求分发处理这一层有差异,而业务层和持久层都是无差异的。因此用springboot 实现graphql API就是把controller改造成resolver。
引入graph maven依赖
参考官网地址:Getting started - GraphQL Java Kickstart
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>11.0.0</version>
</dependency>
<!--自定义字段类型(因为graph标量类型在大多数情况下都不满足项目需要,
比如Date类型,这个就需要自行定义)
-->
<!--官网地址:https://github.com/graphql-java/graphql-java-extended-scalars-->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-extended-scalars</artifactId>
<version>16.0.0</version>
</dependency>
注:如果jar下载不下来,可以配置三方仓库,这个请参照官网。
项目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>2.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.fu</groupId>
<artifactId>springboot-graphql-server-kick</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-graphql-server-kick</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>11.0.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-extended-scalars</artifactId>
<version>16.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
编写第一个demo程序
定义查询schame结构
#声明自定义数据类型
scalar Long
schema {#定义查询
query: UserQuery
mutation: UserMutaions
}
"用户查询"
type UserQuery{#定义查询类型
#getUser要和编写的resolver中的方法名保持一致,参数名可以不一样,只要类型一致即可,否则调用报错
"根据id查询用户"
getUser(userId:Long):SysUserVO
"列表分页查询用户"
listUser(pageNum:Int,pageSize:Int):ListResultModel
}
"用户变更"
type UserMutaions{#定义增删改类型
"新增用户"
addUser(addUser:SysUserSaveDTO):ResultModel
"修改用户"
updateUser(updateUser:SysUserSaveDTO):ResultModel
}
"用户返回对象"
type SysUserVO{#定义返回对象
"用户id"
userId:Long
"用户名称"
userName:String
"用户账号"
nickName:String
"用户性别"
sex:String
}
"列表查询返回对象"
type ListResultModel{#定义返回对象
"编码:200-成功"
code:Int
"提示信息"
msg:String
"列表数据"
rows:[SysUserVO]
"总条数"
total:Int
"总页数"
pages:Int
"当前页"
currentPage:Int
}
"变更返回对象"
type ResultModel{#定义返回对象
"编码:200-成功"
code:Int
"提示信息"
msg:String
"异常信息"
excp:String
}
"新增修改请求对象"
input SysUserSaveDTO{#定义新增对象
"用户id"
userId:Long
"用户名称"
userName:String
"用户账号"
nickName:String
"用户密码"
password:String
"用户性别"
sex:String
}
PS:定义的对象和实体类字段必须保持一样,否则在获取参数或者转换可能报错。
实现用户查询resolver:
新建的类实现GraphQLQueryResolver接口即可实现graphql查询。
package com.fu.resolver;
import com.fu.common.domain.ListResultModel;
import com.fu.domain.entity.SysUser;
import com.fu.domain.vo.SysUserVO;
import com.fu.service.ISysUserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import graphql.kickstart.tools.GraphQLQueryResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 用户查询解析器
* @create: 2021-11-30 15:13
**/
@Component
public class UserQueryResolver implements GraphQLQueryResolver {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ISysUserService sysUserService;
/**根据查询用户*/
public SysUserVO getUser(Long userId) {
SysUser user = sysUserService.getUser(userId);
return new SysUserVO(user);
}
/**列表查询用户*/
public ListResultModel<SysUserVO> listUser(Integer pageNum, Integer pageSize) {
ListResultModel<SysUserVO> result = new ListResultModel<>();
try {
PageHelper.startPage(pageNum, pageSize);
List<SysUser> dataList = sysUserService.listUser(new SysUser());
if (dataList != null && dataList.size() > 0) {
List<SysUserVO> datas = new ArrayList<>();
dataList.forEach(v -> datas.add(new SysUserVO(v)));
PageInfo pageInfo = new PageInfo(dataList);
result = new ListResultModel(pageInfo, datas);
}
} catch (Exception e) {
ListResultModel.getErrorResult(result, e);
logger.error("UserQueryResolver-listUser-error", e);
}
return result;
}
}
实现用户增删改resolver:
新建的类实现GraphQLMutationResolver接口即可。
package com.fu.resolver;
import com.fu.common.domain.ResultModel;
import com.fu.domain.dto.SysUserSaveDTO;
import com.fu.domain.entity.SysUser;
import com.fu.service.ISysUserService;
import graphql.kickstart.tools.GraphQLMutationResolver;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @description: 用户增删改查分析器
* @create: 2021-11-30 15:19
**/
@Component
public class UserMutationResolver implements GraphQLMutationResolver {
@Autowired
private ISysUserService sysUserService;
/**新增用户*/
public ResultModel addUser(SysUserSaveDTO dto) {
System.out.println("新增参数:" + dto.toString());
SysUser user = new SysUser();
BeanUtils.copyProperties(dto, user);
sysUserService.saveUser(user);
return new ResultModel(200, "新增成功", "");
}
/**修改用户*/
public ResultModel updateUser(SysUserSaveDTO dto) {
System.out.println("修改参数:" + dto.toString());
if (dto.getUserId() == null) {
return new ResultModel(200, "修改失败:用户id为空", "");
}
SysUser user = new SysUser();
BeanUtils.copyProperties(dto, user);
sysUserService.saveUser(user);
return new ResultModel(200, "修改成功", "");
}
}
PS:其实可以查询和增删改resolver写成一个也是没有问题的,但是graphql规范查询和变更定义是不一样的,所以为了规范写了两个。
底层实现就不贴代码了,有兴趣的小伙伴请移驾
空白/springboot-graphql - Gitee.com
启动项目测试
查看接口文档
根据id查询用户
分页列表查询用户
新增用户
再次查询看添加数据是否有了:
修改用户
再次查询看数据是否修改了:
参考网址
GraphQL官网:GraphQL | A query language for your API
博客:Springboot中集成GraphQL_chen_duochuang的博客-CSDN博客
官网地址:About GraphQL Spring Boot - GraphQL Java Kickstart
自定义scalar:Spring Boot GraphQL 实战 02_增删改查和自定义标量 - Coder小黑 - 博客园
扩展scalar官网地址:https://github.com/graphql-java/graphql-java-extended-scalars
项目中遇到的问题
graphql增删改跨域问题
springboot2.6pagehelper循环依赖
现象描述:springboot2.6.0 pagehelper-spring-boot-starter PageHelperAutoConfiguration报循环依赖错误
解决办法:
第一种:网上百度的都说,在SpringBoot Application启动类中“@SpringBootApplication”添加extend如下
@SpringBootApplication(exclude = {PageHelperAutoConfiguration.class})。这种方式让项目正常运行了,但是分页也失效了。
第二种:降低springBoot版本,我降到2.5.6然后启动ok,分页ok
第三种(推荐):升级pagehelper-spring-boot-starter 版本到1.4.1,这个还是摸到github官网上看到更新日志的,如下
项目启动正常运行,分页ok
总结
API 的查询语言。
和RESTful核心差异资源的描述信息与其获取方式相分离。
RESTful服务端决定返回结果,GraphQL客户端决定返回结果。
RESTful和GraphQL都是返回json。
更多推荐
所有评论(0)