spring-graphql
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpring for GraphQL - Quick Reference
Spring for GraphQL - 快速参考
Full Reference: See advanced.md for DataLoader configuration, custom scalars, pagination implementation, GraphQL testing patterns, and subscription controllers.
Deep Knowledge: Usewith technology:mcp__documentation__fetch_docsfor comprehensive documentation.spring-graphql
完整参考:查看advanced.md获取DataLoader配置、自定义标量、分页实现、GraphQL测试模式以及订阅控制器相关内容。
深度知识:使用并指定technology参数为mcp__documentation__fetch_docs,即可获取完整文档。spring-graphql
Dependencies
依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>Configuration
配置
yaml
spring:
graphql:
graphiql:
enabled: true
path: /graphiql
schema:
locations: classpath:graphql/**/
path: /graphql
websocket:
path: /graphqlyaml
spring:
graphql:
graphiql:
enabled: true
path: /graphiql
schema:
locations: classpath:graphql/**/
path: /graphql
websocket:
path: /graphqlSchema Definition
Schema定义
graphql
type Query {
bookById(id: ID!): Book
allBooks: [Book!]!
}
type Mutation {
createBook(input: CreateBookInput!): Book!
}
type Book {
id: ID!
title: String!
author: Author!
}
input CreateBookInput {
title: String!
authorId: ID!
}graphql
type Query {
bookById(id: ID!): Book
allBooks: [Book!]!
}
type Mutation {
createBook(input: CreateBookInput!): Book!
}
type Book {
id: ID!
title: String!
author: Author!
}
input CreateBookInput {
title: String!
authorId: ID!
}Query Controller
查询控制器
java
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument String id) {
return bookRepository.findById(id).orElse(null);
}
@QueryMapping
public List<Book> allBooks() {
return bookRepository.findAll();
}
@SchemaMapping(typeName = "Book", field = "author")
public Author author(Book book) {
return authorRepository.findById(book.getAuthorId()).orElse(null);
}
}java
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument String id) {
return bookRepository.findById(id).orElse(null);
}
@QueryMapping
public List<Book> allBooks() {
return bookRepository.findAll();
}
@SchemaMapping(typeName = "Book", field = "author")
public Author author(Book book) {
return authorRepository.findById(book.getAuthorId()).orElse(null);
}
}Mutation Controller
变更控制器
java
@Controller
public class BookMutationController {
@MutationMapping
public Book createBook(@Argument CreateBookInput input) {
return bookService.create(input);
}
}java
@Controller
public class BookMutationController {
@MutationMapping
public Book createBook(@Argument CreateBookInput input) {
return bookService.create(input);
}
}BatchMapping (Solve N+1)
BatchMapping(解决N+1问题)
java
@Controller
public class OptimizedBookController {
@BatchMapping
public Map<Book, Author> author(List<Book> books) {
List<String> authorIds = books.stream()
.map(Book::getAuthorId)
.distinct()
.toList();
Map<String, Author> authorsById = authorRepository.findAllById(authorIds)
.stream()
.collect(Collectors.toMap(Author::getId, a -> a));
return books.stream()
.collect(Collectors.toMap(
book -> book,
book -> authorsById.get(book.getAuthorId())
));
}
}java
@Controller
public class OptimizedBookController {
@BatchMapping
public Map<Book, Author> author(List<Book> books) {
List<String> authorIds = books.stream()
.map(Book::getAuthorId)
.distinct()
.toList();
Map<String, Author> authorsById = authorRepository.findAllById(authorIds)
.stream()
.collect(Collectors.toMap(Author::getId, a -> a));
return books.stream()
.collect(Collectors.toMap(
book -> book,
book -> authorsById.get(book.getAuthorId())
));
}
}Input Validation
输入校验
java
@MutationMapping
public Book createBook(@Argument @Valid CreateBookInput input) {
return bookService.create(input);
}
public record CreateBookInput(
@NotBlank @Size(min = 1, max = 200) String title,
@NotNull String authorId
) {}java
@MutationMapping
public Book createBook(@Argument @Valid CreateBookInput input) {
return bookService.create(input);
}
public record CreateBookInput(
@NotBlank @Size(min = 1, max = 200) String title,
@NotNull String authorId
) {}Error Handling
错误处理
java
@Component
public class CustomExceptionResolver extends DataFetcherExceptionResolverAdapter {
@Override
protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {
if (ex instanceof BookNotFoundException) {
return GraphqlErrorBuilder.newError(env)
.errorType(ErrorType.NOT_FOUND)
.message(ex.getMessage())
.build();
}
return null;
}
}java
@Component
public class CustomExceptionResolver extends DataFetcherExceptionResolverAdapter {
@Override
protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {
if (ex instanceof BookNotFoundException) {
return GraphqlErrorBuilder.newError(env)
.errorType(ErrorType.NOT_FOUND)
.message(ex.getMessage())
.build();
}
return null;
}
}Security
安全
java
@Controller
public class SecuredBookController {
@QueryMapping
@PreAuthorize("hasRole('USER')")
public List<Book> allBooks() {
return bookRepository.findAll();
}
@MutationMapping
@PreAuthorize("hasRole('ADMIN')")
public Book createBook(@Argument CreateBookInput input) {
return bookService.create(input);
}
}java
@Controller
public class SecuredBookController {
@QueryMapping
@PreAuthorize("hasRole('USER')")
public List<Book> allBooks() {
return bookRepository.findAll();
}
@MutationMapping
@PreAuthorize("hasRole('ADMIN')")
public Book createBook(@Argument CreateBookInput input) {
return bookService.create(input);
}
}When NOT to Use This Skill
何时不应使用本技能
- REST APIs - Use standard Spring MVC controllers
- Standalone GraphQL - Use graphql-java directly
- Simple CRUD - May be overkill, consider REST
- File uploads - GraphQL isn't optimized for large binary data
- REST API - 请使用标准Spring MVC控制器
- 独立GraphQL服务 - 直接使用graphql-java
- 简单CRUD场景 - 可能大材小用,建议考虑REST
- 文件上传场景 - GraphQL不适合处理大型二进制数据
Anti-Patterns
反模式
| Anti-Pattern | Problem | Solution |
|---|---|---|
| No @BatchMapping | N+1 queries on nested fields | Use @BatchMapping or DataLoader |
| Unbounded lists | Memory exhaustion | Implement pagination |
| Exposing entities | Schema tightly coupled to DB | Use DTOs/projections |
| No error handling | Stack traces exposed | Custom ExceptionResolver |
| GraphiQL in prod | Security risk | Disable in production |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 未使用@BatchMapping | 嵌套字段出现N+1查询问题 | 使用@BatchMapping或DataLoader |
| 无限制返回列表 | 内存耗尽 | 实现分页 |
| 直接暴露实体 | Schema与数据库强耦合 | 使用DTO/投影 |
| 未做错误处理 | 栈跟踪信息泄露 | 自定义ExceptionResolver |
| 生产环境启用GraphiQL | 安全风险 | 生产环境关闭该功能 |
Quick Troubleshooting
快速故障排查
| Problem | Diagnostic | Fix |
|---|---|---|
| N+1 queries | Check SQL logs | Add @BatchMapping |
| Field not resolved | Check method name | Verify @SchemaMapping matches schema |
| Subscription not working | Check WebSocket config | Enable WebSocket support |
| Validation not applied | Check @Valid | Add @Validated to controller |
| Auth not working | Check security config | Add @PreAuthorize annotations |
| 问题 | 排查方式 | 修复方案 |
|---|---|---|
| N+1查询 | 检查SQL日志 | 添加@BatchMapping |
| 字段无法解析 | 检查方法名 | 验证@SchemaMapping与Schema定义匹配 |
| 订阅不工作 | 检查WebSocket配置 | 开启WebSocket支持 |
| 校验未生效 | 检查@Valid注解是否添加 | 给控制器添加@Validated注解 |
| 鉴权不生效 | 检查安全配置 | 添加@PreAuthorize注解 |
Best Practices
最佳实践
| Do | Don't |
|---|---|
| Use @BatchMapping for N+1 | Fetch nested data individually |
| Define clear schema contracts | Over-expose internal models |
| Implement pagination | Return unbounded lists |
| Use input types for mutations | Use many scalar arguments |
| Add proper error handling | Expose stack traces |
| 正确做法 | 错误做法 |
|---|---|
| 针对N+1问题使用@BatchMapping | 单独获取嵌套数据 |
| 定义清晰的Schema契约 | 过度暴露内部模型 |
| 实现分页 | 返回无限制列表 |
| 变更操作使用输入类型 | 使用多个标量参数 |
| 添加完善的错误处理 | 暴露栈跟踪信息 |
Production Checklist
生产环境检查清单
- Schema well defined
- N+1 solved with BatchMapping
- Input validation enabled
- Error handling configured
- Security annotations applied
- Pagination implemented
- GraphiQL disabled in prod
- Query complexity limits
- Introspection controlled
- Schema定义清晰
- 已通过BatchMapping解决N+1问题
- 已启用输入校验
- 已配置错误处理
- 已应用安全注解
- 已实现分页
- 生产环境已关闭GraphiQL
- 已设置查询复杂度限制
- 内省操作已受控