在当今的软件开发领域,API 已经成为了连接不同系统和应用之间的重要桥梁。随着技术的不断发展和创新,API 的设计和使用方式也在不断演变。其中,GraphQL 作为一种新兴的 API 查询语言,引起了广泛的关注和讨论。那么,我们是否一定要学习 GraphQL 呢?本文将从多个角度探讨 GraphQL 的学习必要性和应用场景,帮助大家更好地理解和评估 GraphQL 的价值。
GraphQL 和传统 REST API 在设计和功能方面存在显著的差异。首先,GraphQL 是一种查询语言,而 REST 是一种基于网络的软件架构概念。它们在处理客户端与服务器间数据请求的方式上也存在很大的不同。
在传统的 RESTful 架构下,后端开发人员定义在各个 URL 的资源上返回的数据,而不是由前端开发人员来提出数据需求。这经常会导致前端需要请求一个资源中所有的信息,即便实际上只需要其中的一部分数据,这种问题被称为过度获取(overfetching)。此外,多个客户端应用可能需要请求多个资源,从而发起多个网络请求,这不仅会造成过度获取的问题,也会导致瀑布式的网络请求(waterfall network requests)。
相比之下,GraphQL 提供了一种更灵活的数据查询方式。它允许客户端精确地指定所需的数据,避免了过度请求和数据冗余的问题。此外,GraphQL 支持嵌套查询和关联查询,使得数据的获取更加灵活和高效。而且,GraphQL 的设计原则是渐进式的改进其 API,无论是添加新 API 或是新增字段,对现有用户都是无感知的,无需划分版本并打断使用旧版本 API 的用户。
graphql-java是GraphQL的java实现形式。graphql-java-spring-boot-starter-webmvc是为了支持SpringWeb方式请求。
而graphql-java-kickstart是在这之上,不仅包含graphql-java,还有graphql-java-tools、graphql-java-servlet。支持了图形界面,我们只需要引入这一个依赖就可以了。
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>15.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
增加配置:
spring:
graphql:
graphiql:
enabled: true
public class Book {
private String id;
private String name;
private int pageCount;
private String authorId;
public Book(String id, String name, int pageCount, String authorId) {
this.id = id;
this.name = name;
this.pageCount = pageCount;
this.authorId = authorId;
}
private static List<Book> books = Arrays.asList(
new Book("book-1", "第1部", 223, "author-1"),
new Book("book-2", "第2部", 635, "author-2"),
new Book("book-3", "第3部", 371, "author-3")
);
public static Book getById(String id) {
return books.stream().filter(book -> book.getId().equals(id)).findFirst().orElse(null);
}
public String getId() {
return id;
}
public String getAuthorId() {
return authorId;
}
}
public class Author {
private String id;
private String firstName;
private String lastName;
public Author(String id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
private static List<Author> authors = Arrays.asList(
new Author("author-1", "Joanne", "Rowling"),
new Author("author-2", "Herman", "Melville"),
new Author("author-3", "Anne", "Rice")
);
public static Author getById(String id) {
return authors.stream().filter(author -> author.getId().equals(id)).findFirst().orElse(null);
}
public String getId() {
return id;
}
}
创建和编辑文件schema.graphqls:
Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
创建book api:
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument String id) {
return Book.getById(id);
}
@SchemaMapping
public Author author(Book book) {
return Author.getById(book.getAuthorId());
}
}
其中,@QueryMapping标注的方法需要和schema.graphqls中Query中的方法一致,带有 @SchemaMapping 注解的方法则成为查询嵌套字段的处理程序。这意味着,当你需要对某个字段进行更深层次的操作时,就可以使用 @SchemaMapping
此时,我们打开http://localhost:8080/graphiql?path=/graphql,进行查询:
在国内大多数场景中,开发者一般会选择使用 MyBatis 或者 MyBatis Plus 生态进行数据操作,而不是选择使用 JPA。
我们在 GraphQL Resolver 中,通过 @Autowired 注入对应的 MyBatis Service,并调用其方法来获取数据。
先引入mybatis plus:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本号</version>
</dependency>
例如,假设有一个 UserService 类,用于处理用户相关的操作:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getAllUsers() {
return userMapper.selectAllUsers();
}
}
然后在 GraphQL Resolver 中注入 UserService,并调用其方法:
@Component
public class UserQuery implements GraphQLQueryResolver {
@Autowired
private UserService userService;
@Override
public List<User> getAllUsers() {
return userService.getAllUsers();
}
}
这样,在执行 GraphQL 查询时,就可以通过 UserQuery 类的 getAllUsers() 方法获取所有用户的数据了。