Java案例如何实现GraphQL?

wen python案例 3

本文目录导读:

Java案例如何实现GraphQL?

  1. 使用 GraphQL Java (官方库)
  2. 使用 Spring Boot + GraphQL (更常见)
  3. 主要优势
  4. 最佳实践

在Java中实现GraphQL主要有几种主流方案,我来详细介绍最常用的两种方法:

使用 GraphQL Java (官方库)

依赖配置 (Maven)

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java</artifactId>
    <version>21.3</version>
</dependency>

完整案例:图书管理系统

// 1. 数据模型
@Data
@AllArgsConstructor
class Book {
    private String id;
    private String title;
    private String author;
    private int pages;
}
@Data
@AllArgsConstructor
class Author {
    private String id;
    private String name;
    private int age;
}
// 2. 数据源 (模拟数据库)
class DataSource {
    private static List<Book> books = Arrays.asList(
        new Book("1", "Java编程思想", "Bruce Eckel", 880),
        new Book("2", "Spring实战", "Craig Walls", 420),
        new Book("3", "算法导论", "Thomas Cormen", 1312)
    );
    private static List<Author> authors = Arrays.asList(
        new Author("1", "Bruce Eckel", 65),
        new Author("2", "Craig Walls", 50)
    );
    public static Book getBookById(String id) {
        return books.stream()
            .filter(b -> b.getId().equals(id))
            .findFirst()
            .orElse(null);
    }
}
// 3. GraphQL Schema 定义和查询执行
public class GraphQLServer {
    public static void main(String[] args) throws Exception {
        // 定义 Schema (可使用SDL字符串)
        String schema = """
            type Book {
                id: ID!
                title: String!
                author: String!
                pages: Int!
            }
            type Query {
                bookById(id: ID!): Book
                books: [Book]
            }
            """;
        // 创建 Schema 对象
        SchemaParser schemaParser = new SchemaParser();
        TypeDefinitionRegistry typeRegistry = schemaParser.parse(schema);
        // 创建 RuntimeWiring
        RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring()
            .type("Query", builder -> builder
                .dataFetcher("bookById", env -> {
                    String id = env.getArgument("id");
                    return DataSource.getBookById(id);
                })
                .dataFetcher("books", env -> DataSource.getBooks())
            )
            .build();
        // 生成可执行的 Schema
        SchemaGenerator generator = new SchemaGenerator();
        GraphQLSchema graphQLSchema = generator.makeExecutableSchema(typeRegistry, wiring);
        // 创建 GraphQL 执行引擎
        GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build();
        // 执行查询
        String query = """
            query {
                bookById(id: "1") {
                    title
                    author
                    pages
                }
            }
            """;
        ExecutionInput executionInput = ExecutionInput.newExecutionInput()
            .query(query)
            .build();
        ExecutionResult result = graphQL.execute(executionInput);
        System.out.println(result.getData().toString());
    }
}

使用 Spring Boot + GraphQL (更常见)

依赖配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Schema 定义 (resources/graphql/schema.graphqls)

type Book {
    id: ID! String!
    author: String!
    pages: Int!
    price: Float!
    reviews: [Review]
}
type Review {
    id: ID!
    rating: Int!
    comment: String
    user: String!
}
type Query {
    books(author: String, titleContains: String): [Book]
    bookById(id: ID!): Book
    reviews(bookId: ID!): [Review]
}
type Mutation {
    addBook(title: String!, author: String!, pages: Int!, price: Float!): Book
    addReview(bookId: ID!, rating: Int!, comment: String, user: String!): Review
}

模型类

// Book.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private String id;
    private String title;
    private String author;
    private int pages;
    private double price;
    private List<Review> reviews;
}
// Review.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Review {
    private String id;
    private int rating;
    private String comment;
    private String user;
}

数据层

// BookRepository.java
@Repository
public class BookRepository {
    private final Map<String, Book> books = new ConcurrentHashMap<>();
    public BookRepository() {
        // 初始化数据
        Book book1 = new Book("1", "Java编程思想", "Bruce Eckel", 880, 79.0, new ArrayList<>());
        Book book2 = new Book("2", "Spring实战", "Craig Walls", 420, 59.0, new ArrayList<>());
        books.put("1", book1);
        books.put("2", book2);
    }
    public List<Book> findAll() {
        return new ArrayList<>(books.values());
    }
    public Book findById(String id) {
        return books.get(id);
    }
    public Book save(Book book) {
        String id = String.valueOf(books.size() + 1);
        book.setId(id);
        books.put(id, book);
        return book;
    }
    public List<Book> findByAuthor(String author) {
        return books.values().stream()
            .filter(b -> b.getAuthor().equalsIgnoreCase(author))
            .collect(Collectors.toList());
    }
}
// ReviewRepository.java
@Repository
public class ReviewRepository {
    private final Map<String, List<Review>> reviews = new ConcurrentHashMap<>();
    public ReviewRepository() {
        // 初始化评论数据
        reviews.put("1", Arrays.asList(
            new Review("1", 5, "经典著作", "张三"),
            new Review("2", 4, "内容深入", "李四")
        ));
    }
    public List<Review> findByBookId(String bookId) {
        return reviews.getOrDefault(bookId, new ArrayList<>());
    }
    public Review save(String bookId, Review review) {
        String id = UUID.randomUUID().toString();
        review.setId(id);
        reviews.computeIfAbsent(bookId, k -> new ArrayList<>()).add(review);
        return review;
    }
}

GraphQL 控制器

// BookController.java
@Controller
public class BookController {
    @Autowired
    private BookRepository bookRepository;
    @Autowired
    private ReviewRepository reviewRepository;
    @QueryMapping
    public List<Book> books(@Argument String author, @Argument String titleContains) {
        if (author != null) {
            return bookRepository.findByAuthor(author);
        }
        if (titleContains != null) {
            return bookRepository.findAll().stream()
                .filter(b -> b.getTitle().toLowerCase().contains(titleContains.toLowerCase()))
                .collect(Collectors.toList());
        }
        return bookRepository.findAll();
    }
    @QueryMapping
    public Book bookById(@Argument String id) {
        return bookRepository.findById(id);
    }
    @QueryMapping
    public List<Review> reviews(@Argument String bookId) {
        return reviewRepository.findByBookId(bookId);
    }
    @MutationMapping
    public Book addBook(@Argument String title, @Argument String author, 
                       @Argument int pages, @Argument double price) {
        Book book = new Book(null, title, author, pages, price, new ArrayList<>());
        return bookRepository.save(book);
    }
    @MutationMapping
    public Review addReview(@Argument String bookId, @Argument int rating,
                           @Argument String comment, @Argument String user) {
        Review review = new Review(null, rating, comment, user);
        return reviewRepository.save(bookId, review);
    }
    // 为Book类型添加reviews的关联查询
    @SchemaMapping(typeName = "Book", field = "reviews")
    public List<Review> getReviews(Book book) {
        return reviewRepository.findByBookId(book.getId());
    }
}

应用配置 (application.yml)

spring:
  graphql:
    graphiql:
      enabled: true  # 开启GraphiQL测试界面
    schema:
      locations: classpath:graphql/

测试查询示例

访问 http://localhost:8080/graphiql 打开GraphiQL界面,可以执行以下查询:

# 查询所有书籍
query {
  books {
    id    author
    pages
    price
  }
}
# 带参数的查询
query {
  bookById(id: "1") {    author
    reviews {
      rating
      comment
      user
    }
  }
}
# 新增书籍
mutation {
  addBook(title: "Python编程", author: "Guido", pages: 500, price: 69.0) {
    id  }
}
# 新增评论
mutation {
  addReview(bookId: "1", rating: 5, comment: "非常好的一本书", user: "王五") {
    id
    rating
  }
}

主要优势

  1. 精确的数据获取:客户端只需要请求需要的数据
  2. 单一端点:所有接口都通过 /graphql 端点访问
  3. 强类型系统:Schema定义明确了数据类型和关系
  4. 版本控制友好:通过Schema演进,不需要版本号

最佳实践

  1. N+1查询问题:使用DataLoader解决
  2. 安全性:实现深度限制、查询复杂度分析
  3. 缓存策略:合理使用HTTP缓存和持久化缓存
  4. 错误处理:统一的错误格式和异常处理

这个案例展示了Java实现GraphQL的基本方式,可以根据实际需求进行扩展和优化。

抱歉,评论功能暂时关闭!