历史

       GraphQL是由Facebook创造的

当时,Facebook想在移动端实现新闻推送,这不像检索一个故事、作者、故事的内容、评论列表和喜欢该文章的人这么简单,而是每个故事都相互关联、嵌套和递归的。现有的API没有被设计成允许开发人员在移动设备上展示一个丰富、类似新闻推送的体验。它们没有层次性,允许开发人员选择他们所需要的,或有显示异构推送故事列表的能力。因此,2012年Facebook决定自己构建一个新的新闻推送API,这就是GraphQL形成时间。同年 8 月中旬,Facebook 发布了采用新 GraphQL 技术的 iOS5.0 应用程序。它允许开发人员通过利用其数据获取(data-fetching)功能来减少网络的使用。在接下来的一年半时间里,除了新闻推送外,GraphQL API 扩展到大多数的 FacebookiOS 应用程序。在 2015 年,GraphQL 规范首次与 JavaScript 中的引用实现一起发布

参考网址:

GraphQL 的前世今生 - .NET西安社区 - 博客园

谈谈 GraphQL 的历史、组件和生态系统 - 知乎

是什么?

官网地址: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: IDtitle: Stringpublished: Dateprice: Stringauthor: Author
}
type Author {id: IDfirstName: StringlastName: Stringbooks: [Book]
}
#2.创建query
type Query {book(id: ID!): Bookauthor(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实现方式:

  1. 通过/user/<id>获取初始用户数据
  2. 通过/user/<id>/posts 返回用户的所有帖子
  3. 请求/user/<id>/followers,返回每个用户的关注者列表

现在有个要求:要获取某个用户信息和用户所有的帖子以及用户关注者信息。

GraphQL实现方式:

结论:RESTful 请求了3次达到目的,并且接口返回了很多并不需要的数据。GraphQL只请求了一次,并且返回的结果是必需的。

有什么用?

官网解释

  1. 请求你所要的数据不多不少。
  2. 获取多个资源只用一个请求。
  3. 描述所有的可能类型系统。
  4. 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:Stringage:Intdept:DeptVO
}type DeptVO{deptId:LongdeptName: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:UserQuerymutation: userMutations
}"用户变更"
type userMutations{"添加用户"addUser(addUser:UserSaveDTO):ResultModel
}"新增用户请求对象"
input UserSaveDTO{#定义新增对象"用户名称"userName:String!"用户年龄"age:Int!
}type ResultModel{#定义返回对象code:Intmsg:Stringecxp:String
}type UserQuery{#定义查询类型getUser(userId:Long):UserVO #指定对象以及参数类型}type UserVO{ #定义对象userId:Long! #!表示属性是非空项userName:Stringage:Intdept:DeptVO
}type DeptVO{deptId:LongdeptName: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:Stringage: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;@Autowiredprivate List<MyDataFetcher> myDataFetcherList;@PostConstructpublic void init() throws FileNotFoundException {File file = ResourceUtils.getFile("classpath:graphqls/user.graphql");this.graphQL = GraphQL.newGraphQL(buidlGraphQLSchema(file)).build();}@Beanpublic 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 {@Autowiredprivate 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: UserQuerymutation: 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());@Autowiredprivate 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 {@Autowiredprivate 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。

GraphQL入门基础篇教程相关推荐

  1. python视频教程推荐it教程网_Python视频教程之入门基础篇_IT教程网

    资源名称:Python视频教程之入门基础篇 资源目录: [IT教程网]320b96cae58124db5fb6e7c5df99aefc [IT教程网]699434136852f34ec720f2a34 ...

  2. 【目录】Python 入门基础篇 <(^-^)>

    Python 入门基础篇 一.关于Python的介绍与准备工作 Python基础介绍 Jupyter notebook基础介绍 Jupyter notebook打开问题 Jupyter noteboo ...

  3. typescript箭头函数参数_Typescript 入门基础篇(一)

    Typescript 基础 Typescript是Javascript的一个超集.以下typescript简称为ts, 此文章主要是对ts官网文档的一个简化,缩短学习基础时间. 类型基础 ts 的类型 ...

  4. 小程序开发入门基础篇-张代浩-专题视频课程

    小程序开发入门基础篇-1995人已学习 课程介绍         采用小程序wepy框架,初级讲解如何搭建小程序的开发环境,创建工程,语法介绍.开发调试等,课程采用实战代码案例作为教材,通俗易懂,简单 ...

  5. [转载] 把数据对象转成字符串_python入门基础培训教程,Python 字符串学习

    参考链接: Python字符串| printable python入门基础培训教程中,字符串的学习即基础又非常重要,如果不能掌握好python字符串,对于以后python课程的学习,将会带来严重的错误 ...

  6. Python入门基础篇 No.8 —— 时间的表示_unix时间点_毫秒_time模块

    Python入门基础篇 No.8 -- 时间的表示_unix时间点_毫秒_time模块 文章目录 Python入门基础篇 No.8 -- 时间的表示_unix时间点_毫秒_time模块 前言 一.时间 ...

  7. css基础知识入门教程免费,DIVCSS入门基础知识教程.doc

    DIVCSS入门基础知识教程 HYPERLINK "/rumen/r319.html" DIV+CSS盒子模型 CSS盒子模型-什么是 HYPERLINK "/" ...

  8. vue实战入门基础篇五:从零开始仿门户网站实例-关于我们实现

    上一篇:vue实战入门基础篇四:从零开始仿门户网站实例-网站首页实现https://blog.csdn.net/m0_37631110/article/details/123045334 一.目录 第 ...

  9. Vue2基础篇教程合集

    Vue2基础篇教程合集 点击跳转具体教程,该教程基于HTML书写,深度理解Vue如何运行. 一.Vue2基础篇-初识Vue 二.Vue2基础篇-模板语法 三.Vue2基础篇-理解MVVM模型 三.Vu ...

最新文章

  1. 数据库设计Step by Step (9)——ER-to-SQL转化
  2. jota-time 练习
  3. 11.python并发入门(part4 死锁与递归锁)
  4. Openwrt中ppp拨号总结
  5. python数字转中文字符_Python实现中文数字转换为阿拉伯数字的方法示例
  6. 右键单击文件夹进入命令行窗口
  7. Java JDBC工具类
  8. python合并多个excel代码_Python脚本合并多个Excel文件
  9. https理论与实践
  10. FreeBSD BSDiff 被曝高危内存损坏漏洞,时隔4年终修复
  11. Spring Security示例教程
  12. Linux正确查看内存占用
  13. 为什么要对数据仓库分层
  14. 抖音去水印java和js版本
  15. java中的打印pdf_Java 打印PDF文档
  16. Foxmail的创建
  17. 为什么说c语言是关键字,为什么说C语言既有高级语言又有低级语言的特点
  18. 百度地图精准定位,自定义marker,自定义信息弹出窗口。
  19. Fredman构造法构造完备哈希
  20. 品牌公关与传统媒体有什么关联和作用

热门文章

  1. Hadoop之SSH无密登录配置
  2. 在家用手机薅羊毛也能挣点零花钱
  3. #千锋逆战班,接口回调#
  4. oracle数据库行转化成列,ORACLE行转列(行转一列,行转多列)
  5. oracleclient和plsql安装注意事项
  6. 网络基础-路由器工作原理
  7. js 任务栏浏览器图标闪烁_电子JS | 任务栏图标
  8. mysql排他锁_mysql共享锁与排他锁
  9. 【VUE】一个简单常用的proxyTable配置
  10. python识别几何图形拼成的图案_自动驾驶汽车视觉- 图像特征提取与匹配技术