GraphQL是一种基于api的查询语言,相对与RAST API更加的高效灵活。在REST实现中,即使你只是想获取其中的部分字段,也会响应给你完整的数据。而Graphql可以按照你的需求获取你想要的字段。
以下我们将使用Spring Boot结合GraphQL进行落地实践。
工程依赖
本次基于Spring Boot 2.4.4版本新建工程,引入依赖包如下:
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.learn</groupId>
<artifactId>learn</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>learn</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<graphql.version>16.2</graphql.version>
<guava.version>29.0-jre</guava.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>${graphql.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
编写schema配置文件
在工程resources目录下新建schema.graphqls文件。该文件中描述了查询方法、参数以及可以返回的结果字段,用户可以按需获取结果字段。
type Query {
orderById(id: ID): Order
commodityByName(name: String): Commodity
}
type Order {
id: ID
customer: String
commodity: Commodity
}
type Commodity {
id: ID
name: String
price: String
}
编写graphqls查询代码
我们先从新建实体类开始。新建Commodity与Order实体,并创建一些数据。代码如下: Commodity:
package com.learn.entity;
import lombok.Builder;
import lombok.Getter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Getter
@Builder
public class Commodity {
private String id;
private String name;
private BigDecimal price;
public static List<Commodity> commoditys = new ArrayList<Commodity>(){{
add(Commodity.builder().id("commodity-1").name("从入门到放弃").price(new BigDecimal(10)).build());
add(Commodity.builder().id("commodity-2").name("从入门到入土").price(new BigDecimal(20)).build());
}};
}
Order:
package com.learn.entity;
import lombok.Builder;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Builder
public class Order {
private String id;
private String customer;
private String commodityId;
private Commodity commodity;
public static List<Order> orders = new ArrayList<Order>(){{
add(Order.builder().id("order-1").customer("张三").commodityId("commodity-2").build());
add(Order.builder().id("order-2").customer("李四").commodityId("commodity-1").build());
}};
}
新建GraphQLService类用于构建GraphQL对象。获取schema文件配置中的QL语句,然后后定义QL语句的解析方式。例如在buildWiring方法中Query的orderById方法是通过graphQLDataFetchers.getOrderByIdDataFetcher()来提供查询的结果数据。
package com.learn.service;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import com.learn.service.fether.GraphQLDataFetchers;
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.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.URL;
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
@Service
public class GraphQLService {
private GraphQL graphQL;
public GraphQL getGraphQL() {
return graphQL;
}
@Autowired
private GraphQLDataFetchers graphQLDataFetchers;
@PostConstruct
public void init() throws IOException {
URL url = Resources.getResource("schema.graphqls");
String sdl = Resources.toString(url, Charsets.UTF_8);
GraphQLSchema graphQLSchema = buildSchema(sdl);
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
private GraphQLSchema buildSchema(String sdl) {
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
RuntimeWiring runtimeWiring = buildWiring();
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query")
.dataFetcher("orderById", graphQLDataFetchers.getOrderByIdDataFetcher()))
.type(newTypeWiring("Order")
.dataFetcher("commodity", graphQLDataFetchers.getCommodityDataFetcher()))
.type(newTypeWiring("Query")
.dataFetcher("commodityByName", graphQLDataFetchers.getCommodityByNameDataFetcher()))
.build();
}
}
新建GraphQLDataFetchers类,用于提供查询数据dataFetchingEnvironment.getArgument获取请求中的查询参数,然后orders中找到与之匹配的order。
下面提供数据的方法的返回都是DataFetcher类型,DataFetcher在执行查询时,调用底层服务查询Order中提取出匹配的结果字段。
package com.learn.service.fether;
import com.learn.entity.Commodity;
import com.learn.entity.Order;
import graphql.schema.DataFetcher;
import org.springframework.stereotype.Component;
@Component
public class GraphQLDataFetchers {
public DataFetcher getOrderByIdDataFetcher() {
return dataFetchingEnvironment -> {
String orderId = dataFetchingEnvironment.getArgument("id");
return Order.orders
.stream()
.filter(order -> order.getId().equals(orderId))
.findFirst()
.orElse(null);
};
}
public DataFetcher getCommodityDataFetcher() {
return dataFetchingEnvironment -> {
Order order = dataFetchingEnvironment.getSource();
String commodityId = order.getCommodityId();
return Commodity.commoditys
.stream()
.filter(commodity -> commodity.getId().equals(commodityId))
.findFirst()
.orElse(null);
};
}
public DataFetcher getCommodityByNameDataFetcher() {
return dataFetchingEnvironment -> {
String commodityName = dataFetchingEnvironment.getArgument("name");
return Commodity.commoditys
.stream()
.filter(commodity -> commodity.getName().equals(commodityName))
.findFirst()
.orElse(null);
};
}
}
新建OrderController,提供数据查询接口。
package com.learn.controller;
import com.learn.service.GraphQLService;
import graphql.ExecutionResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
private GraphQLService graphQLService;
@PostMapping("/crmdrt")
public ResponseEntity<Object> getAllBooks(@RequestBody String query){
ExecutionResult execute = graphQLService.getGraphQL().execute(query);
return new ResponseEntity<>(execute, HttpStatus.OK);
}
}
整个工程的代码就编写完了,整体的工程结构如下:
测试
将项目启动,通过postman访问http:localhost:8080/crmdrt对服务发起请求。
输入1:
{
orderById(id: "order-1") {
id customer commodity {name price}
}
}
输出1:
{
"errors": [],
"data": {
"orderById": {
"id": "order-1",
"customer": "张三",
"commodity": {
"name": "从入门到入土",
"price": "20"
}
}
},
"extensions": null,
"dataPresent": true
}
输入2:
{commodityByName(name: "从入门到入土") {name price}}
输出2:
{
"errors": [],
"data": {
"commodityByName": {
"name": "从入门到入土",
"price": "20"
}
},
"extensions": null,
"dataPresent": true
}
参考文献:https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/