
GraphQL是一种查询语言用来设计 APIs,可替代 REST,SOAP 或 gRPC,详细的可以查看官网解释: GraphQL
本教程中,我们将构建一个 Spring Boot GraphQL 例子并提供 CRUD APIs 来从 mysql 数据库中创建,查询,更新,删除对象。
Sring Boot CRUD GraphQL APIs 概览
目标
我们有两个数据模型:作者(Author) 和 课程(Tutorial)。
1 2 3 4 5 6 7 8 9 10 11 12
| Author { id: Long name: String age: Integer }
Tutorial { id: Long title: String description: String author: Author }
|
一个作者可能有多个课程 , 所以他们是一对多的关系
CRUD GraphQL APIs
创建作者:
GraphQL
1 2 3 4 5 6 7 8
| mutation { createAuthor( name: "bezkoder", age: 27) { id name } }
|
响应
1 2 3 4 5 6 7 8
| { "data": { "createAuthor": { "id": "1", "name": "bezkoder" } } }
|
查询所有作者
GraphQL
1 2 3 4 5 6 7
| query { findAllAuthors{ id name age } }
|
响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| { "data": { "findAllAuthors": [ { "id": 1, "name": "张三", "age": 25 }, { "id": 2, "name": "李四" "age": 43 } ] } }
|
等等,这里先不一一列举了
如果查看数据库,你会发现有两张表: author 和 tutorial
内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| mysql> select * from author; +----+-----+--------+ | id | age | name | +----+-----+--------+ | 1 | 25 | 张三 | | 2 | 43 | 李四 | +----+-----+--------+
mysql> select * from tutorial; +----+-------------------------------------+----------------+-----------+ | id | description | title | author_id | +----+-------------------------------------+----------------+-----------+ | 1 | 该教程可以帮助你学习 Java | 学习 Java | 1 | | 2 | 很棒的 GraphQL 教程 | 学习 GraphQL | 2 | +----+-------------------------------------+----------------+-----------+
|
开始构建APIs
用到的依赖
我们的应用将用到下面这些:
- Java8 或者 java11
- Spring Boot 2.2.1.RELEASE (with Spring Web)
- Graphql-spring-boot-starter 7.0.1
- Graphiql-spring-boot-starter 7.0.1
- Gradle 6.3
- MySQL 5.7
项目结构

- resources/graphql 中所有的
.graphqls 定义了 GraphQL schemas
- model: 存放 Author 和 Tutorial 实体类
- mapper: 存放 Mapper 接口来和 MySQL 交互
- resolver: 通过实现一些
Resolver接口来处理查询和修改的请求,该类可以理解成 SpringMVC 中的 Controller 层
- application.yml: 配置文件
- build.gradle: gradle 的构建文件
项目准备
首先创建 Spring Boot 项目,然后添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' developmentOnly 'org.springframework.boot:spring-boot-devtools' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' }
implementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:7.0.1' implementation 'com.graphql-java-kickstart:graphiql-spring-boot-starter:7.0.1'
implementation 'com.baomidou:mybatis-plus-boot-starter:3.1.2'
implementation 'mysql:mysql-connector-java:5.1.6'
implementation 'org.flywaydb:flyway-core:6.3.3'
compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.spockframework:spock-core:2.0-M1-groovy-2.5' testImplementation 'org.spockframework:spock-spring:2.0-M1-groovy-2.5' }
|
相关配置文件
打开 application.yml 并添加下面的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| server: port: 8083
spring: flyway: locations: classpath:db/migration out-of-order: true url: ${spring.datasource.url} user: ${spring.datasource.username} password: ${spring.datasource.password} datasource: url: jdbc:mysql://localhost:3306/learn_graphql username: root password: driver-class-name: com.mysql.jdbc.Driver
mybatis-plus: type-aliases-package: com.kba977.learngraphql.mapper
|
创建 GraphQL Schema
我们为了将schema拆分到不同的文件里,以分类管理,Spring Boot GraphQL 会自动扫描所有 schema 文件
首先我们创建 Schemas
author.graphqls
1 2 3 4 5 6 7 8 9 10
| type Author { id: ID! name: String! age: Int }
input CreateAuthorInput { name: String! age: Int! }
|
tutorial.graphqls
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type Tutorial { id: ID! title: String! description: String author: Author }
input CreateTutorialInput { title: String! description: String! authorId: ID! }
input UpdateTutorialInput { title: String description: String }
|
query.graphqls
1 2 3 4 5 6 7 8 9 10 11 12
| # Root type Query { # Author findAllAuthors: [Author]! findAuthorById(id: ID!): Author countAuthors: Int!
# Tutorial findAllTutorials: [Tutorial]! findTutorialById(id: ID!): Tutorial countTutorials: Int! }
|
mutation.graphqls
1 2 3 4 5 6 7 8 9 10
| # Root type Mutation { # Author createAuthor(createAuthorInput: CreateAuthorInput!): Author!
# Tutorial createTutorial(createTutorialInput: CreateTutorialInput!): Tutorial! updateTutorial(id: ID!, updateTutorialInput: UpdateTutorialInput): Tutorial! deleteTutorial(id: ID!): Boolean }
|
在 GraphQL 中分为两个大类,所有的查询入口在 Query, 所有的变更入口都在 Mutation
上面文件中 ! 的意思该字段不能为 null,如果加 ! ,那么在返回值的时候 GraphQL允许返回 null
定义实体类,Mapper接口,Service实现
这些我们比较熟悉,直接跳过大家可以查看源码
实现GraphQL Root 解析器
如下面文件 TutorialMutationResolver 实现了对 Tutorial 的变更请求处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package com.kba977.learngraphql.graphql.resolver.mutation;
import com.kba977.learngraphql.graphql.type.tutorial.CreateTutorialInput; import com.kba977.learngraphql.graphql.type.tutorial.UpdateTutorialInput; import com.kba977.learngraphql.model.Tutorial; import com.kba977.learngraphql.service.TutorialService; import graphql.kickstart.tools.GraphQLMutationResolver; import org.apache.ibatis.javassist.NotFoundException; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;
import java.util.Optional;
@Component public class TutorialMutationResolver implements GraphQLMutationResolver {
@Autowired private TutorialService tutorialService;
public Tutorial createTutorial(CreateTutorialInput input) { Tutorial tutorial = new Tutorial(input.getTitle(), input.getDescription(), input.getAuthorId()); tutorialService.save(tutorial); return tutorial; }
public Tutorial updateTutorial(Integer id, UpdateTutorialInput input) throws NotFoundException { Optional<Tutorial> optTutorial = Optional.ofNullable(tutorialService.getById(id));
if (optTutorial.isPresent()) { Tutorial tutorial = optTutorial.get(); BeanUtils.copyProperties(input, tutorial); tutorialService.updateById(tutorial);
return tutorialService.getById(id); } throw new NotFoundException("Not found Tutorial to update!"); }
public boolean deleteTutorial(Integer id) { return tutorialService.removeById(id); } }
|
实现 GraphQL 字段解析器
对于 Tutorial 中的复杂类型 author,我们需要字段解析器去解析它的值,TutorialResolver 实现了 GraphQLResolver 接口并实现 getAuthor() 方法
resolver/TutorialResolver.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.kba977.learngraphql.graphql.resolver;
import com.kba977.learngraphql.model.Author; import com.kba977.learngraphql.model.Tutorial; import com.kba977.learngraphql.service.AuthorService; import graphql.kickstart.tools.GraphQLResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;
import java.util.Optional;
@Component public class TutorialResolver implements GraphQLResolver<Tutorial> {
@Autowired private AuthorService authorService;
public Author getAuthor(Tutorial tutorial) { return authorService.getById(tutorial.getAuthorId()); } }
|
如果客户端请求 Tutorial 没有带上 author 字段,那么 GraphQL Server 将不会调用该方法去解析字段
运行应用并检查结果
应用启动后,打开浏览器访问 localhost:8083/graphiql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| fragment author on Author { id name age }
query findAllAuthors { findAllAuthors { ...author } }
query findAuthorById { findAuthorById(id: 3) { ...author } }
query countAuthors { countAuthors }
fragment tutorial on Tutorial { id title description author { ...author } }
query findAllTutorials { findAllTutorials { ...tutorial } }
query countTutorials { countTutorials }
mutation createAuthor { createAuthor(createAuthorInput: { name: "kba999" age: 20 }) { ...author } }
mutation createTutorial { createTutorial(createTutorialInput: { title: "新概念英语" description: "学习英语的好资料" authorId: 2 }) { ...tutorial } }
mutation updateTutorial { updateTutorial(id: 4, updateTutorialInput: { title: "我们的世界" }) { ...tutorial } }
mutation deleteTutorial { deleteTutorial(id: 1) }
|

进一步学习资源
源代码
你可以从github上找到完整的代码:https://github.com/kba977/learn-graphql