ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

使用 graphql-java 在 Java 后端实现 GraphQL Mutation 的完整指南

使用 graphql-java 在 Java 后端实现 GraphQL Mutation 的完整指南 【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载本篇指南基于开源仓库 howtographql 中 Java 后端教程 的 Mutations 章节系统讲解如何在 graphql-java 技术栈中通过 SDL 定义 mutation、编写带参数的根解析器Root Resolver并将其注册为可执行 schema最终用 GraphiQL 完成增删改验证。读完本文你将掌握 Java 服务端 mutation 从模式定义 → 解析器实现 → 端点注册 → 在线测试的完整闭环并了解 mutation 参数进阶用法与 context 注入等实战细节。先理解mutation 与 query 的本质区别在动手写代码之前先明确一个关键概念在 GraphQL 中mutation 与 query 的语法几乎完全相同二者的差异主要体现在语义层。query 用于读取数据是纯函数式的、无副作用的操作mutation 用于写入数据创建、更新、删除会产生副作用二者在语法结构上一致都有根字段root field、都可以携带参数、都可以在载荷payload中声明要返回的字段。从 GraphQL 核心概念章节 可以看到mutation 一般有创建、更新、删除三类且始终以mutation关键字开头例如mutation { createPerson(name: Bob, age: 36) { name age } }在 Java 服务端实现中mutation 与 query 的落地方式也高度一致Mutation根解析器与Query根解析器一样实现GraphQLRootResolver接口方法名对应 SDL 中的根字段名。接下来就以创建链接Link为例走一遍完整实现流程。第一步在 SDL 中定义 createLink mutationJava 教程采用 schema-firstSDL 优先的开发方式先以文本形式定义 schema再由graphql-java-tools的SchemaParser将 SDL 解析并接线到 Java 解析器。首先在资源目录的 schema 文件教程项目中的src/main/resources/schema.graphqls中为创建链接定义一个 mutation 根字段type Mutation { createLink(url: String!, description: String!): Link }这里的要点url与description都是String!非空标量!表示该参数必填客户端若不传将触发校验错误返回类型是Link即教程此前在 Queries 章节 中定义的对象类型url、description两个非空字段根字段的参数会直接映射到解析器方法的形参见下文第三步因此 SDL 中的参数名、类型必须与 Java 方法保持一致。第二步更新 schema 入口注册 mutation 根类型GraphQL schema 中Query、Mutation以及可选的Subscription是三个特殊的根类型它们是客户端请求的入口点。仅仅定义type Mutation { ... }还不够必须通过schema块显式声明mutation入口指向哪个根类型schema { query: Query mutation: Mutation }把这段定义与之前的type Link、type Query一并保存在schema.graphqls中。若省略schema块中的mutation: Mutation声明客户端发来的 mutation 请求将无法被解析执行——这正如 Getting Started 章节 所述此时启动服务器访问/graphql仍会报错因为定义的根字段没有任何可执行的解析函数。第三步编写带参数的根 Mutation 解析器接下来创建根 mutation 解析器类它与已有的Query类结构完全类似可参考 Queries 章节中的Query实现public class Mutation implements GraphQLRootResolver { private final LinkRepository linkRepository; public Mutation(LinkRepository linkRepository) { this.linkRepository linkRepository; } public Link createLink(String url, String description) { Link newLink new Link(url, description); linkRepository.saveLink(newLink); return newLink; } }值得注意的关键设计方法名即根字段名createLink方法将自动成为 SDL 中createLinkmutation 的解析函数resolver形参与 SDL 参数一一对应createLink(String url, String description)的形参名称和类型与 SDL 中createLink(url: String!, description: String!)的定义保持一致——这正是教程强调的resolvers with arguments模式GraphQL 字段的参数会按名字注入到解析器方法的参数中依赖注入而非静态访问Mutation通过构造器持有LinkRepository把数据存取逻辑隔离在仓库类中便于后续替换为数据库实现见下文持久化进阶。配套的数据层LinkRepository与LinkPOJO 已在 Queries 章节 中创建Link是无行为的 POJO包含url与description字段及其 getterLinkRepository先用内存ArrayList保存链接提供getAllLinks()与saveLink(Link)两个方法public class LinkRepository { private final ListLink links; public LinkRepository() { links new ArrayList(); //add some links to start off with links.add(new Link(http://howtographql.com, Your favorite GraphQL page)); links.add(new Link(http://graphql.org/learn/, The official docks)); } public ListLink getAllLinks() { return links; } public void saveLink(Link link) { links.add(link); } }createLink解析器的执行逻辑非常直观构造新的Link→ 调用linkRepository.saveLink(newLink)持久化 → 将新链接作为返回值交还给客户端。返回新创建的对象意味着客户端可以在同一次往返roundtrip中拿到服务端生成的数据而无需二次查询。第四步在 GraphQLEndpoint#buildSchema 中注册新解析器有了解析器还不够必须把Mutation实例交给SchemaParser让 SDL 与 Java 方法完成接线。在GraphQLEndpoint的buildSchema方法中把Mutation与Query一起注册进 resolvers 列表private static GraphQLSchema buildSchema() { LinkRepository linkRepository new LinkRepository(); return SchemaParser.newParser() .file(schema.graphqls) .resolvers(new Query(linkRepository), new Mutation(linkRepository)) .build() .makeExecutableSchema(); }回顾 Getting Started 章节 中GraphQLEndpoint的初始形态可以清晰地看到 schema 构建逻辑的演进脉络最初的版本只解析schema.graphqls而不注册任何 resolver导致/graphql端点无法执行查询Queries 章节 将 schema 构建逻辑抽取为独立的buildSchema()方法并注册Query解析器本章在此基础上追加Mutation解析器——由于抽取了独立方法新增根解析器只需改动一行这正是教程强调的为未来扩展预留空间的实践。至此schema 已成为一个运行时对象GraphQLSchema其中每个根字段都关联到了对应的 Java 方法。第五步重启 Jetty用 GraphiQL 验证 mutation完成上述修改后在pom.xml所在目录执行mvn jetty:run重启 Jetty默认端口 8080打开 GraphiQL 交互界面其接入方式详见 Queries 章节的 Testing with GraphiQL 小节在左侧面板输入并执行 mutationmutation { createLink(url: http://example.com, description: Example link) { url description } }执行成功后服务端返回新创建的Link对象。随后重新执行allLinks查询{ allLinks { url description } }可以看到响应中除了教程预置的两条初始链接外还包含刚通过 mutation 创建的新链接从而确认数据确实被LinkRepository持久化当前阶段为内存存储服务器重启后即丢失{ data: { allLinks: [ { url: http://howtographql.com, description: Your favorite GraphQL page }, { url: http://graphql.org/learn/, description: The official docks }, { url: http://example.com, description: Example link } ] } }提示由于 GraphiQL 内置自动补全与 schema 文档面板Docs你可以借此快速浏览Mutation根类型下的全部字段验证 schema 定义是否与预期一致。进阶一把内存仓库换成 MongoDB教程在 Connectors 章节 中把LinkRepository从内存列表重构为基于 MongoDB 的实现这一重构对createLinkmutation 的影响被控制在极小的范围内——这正是当初把数据存取逻辑抽离到仓库类的好处public class LinkRepository { private final MongoCollectionDocument links; public LinkRepository(MongoCollectionDocument links) { this.links links; } public ListLink getAllLinks() { ListLink allLinks new ArrayList(); for (Document doc : links.find()) { allLinks.add(link(doc)); } return allLinks; } public void saveLink(Link link) { Document doc new Document(); doc.append(url, link.getUrl()); doc.append(description, link.getDescription()); links.insertOne(doc); } private Link link(Document doc) { return new Link( doc.get(_id).toString(), doc.getString(url), doc.getString(description)); } }与此同时Link类型在 Connectors 章节 中增加了id: ID!字段GraphQLEndpoint改为在静态初始化块中通过new MongoClient().getDatabase(hackernews)获取links集合并注入仓库。改造后createLinkmutation 的解析器代码无需任何改动数据便由内存切换到了 Mongo 持久化——这从侧面印证了 mutation 解析器与存储解耦的价值。进阶二mutation 的输入参数类型与返回载荷createLink只使用了标量参数。当 mutation 需要多个相关参数时更地道的做法是引入input类型。教程在 Authentication 章节 中定义createUsermutation 时即用到了这一模式type Mutation { #The new mutation createUser(name: String!, authProvider: AuthData!): User createLink(url: String!, description: String!): Link } type User { id: ID! name: String! email: String password: String } input AuthData { email: String! password: String! }对应的 Java 解析器方法接收一个AuthData对象作为参数AuthData是含无参构造器和 setter 的普通 POJO以便框架完成反序列化绑定public User createUser(String name, AuthData auth) { User newUser new User(name, auth.getEmail(), auth.getPassword()); return userRepository.saveUser(newUser); }此外More Mutations 章节 展示了更复杂的 mutation 形态——createVote返回包含关联对象User、Link与自定义标量DateTime的Vote类型type Mutation { #the others stay the same createVote(linkId: ID, userId: ID): Vote } type Vote { id: ID! createdAt: DateTime! user: User! link: Link! } scalar DateTime对应实现中createVote解析器通过VoteRepository.saveVote落库并返回带id的新对象public Vote createVote(String linkId, String userId) { ZonedDateTime now Instant.now().atZone(ZoneOffset.UTC); return voteRepository.saveVote(new Vote(now, userId, linkId)); }而Vote中user、link这类非标量字段则需要独立的 companion resolverVoteResolver implements GraphQLResolverVote来按需解析自定义DateTime标量则通过.scalars(Scalars.dateTime)注册进SchemaParser。从这些示例可以总结出 mutation 的通用最佳实践简单参数直接用标量成组参数封装为input类型返回对象中包含关联关系时为数据类配套GraphQLResolverT需要特殊格式如时间戳时注册自定义标量由Coercing负责序列化/反序列化。进阶三通过 context 让 mutation 感知当前用户在很多业务场景中mutation 需要知道是谁发起了这次操作。教程在 Authentication 章节 中展示了标准解法用自定义AuthContext extends GraphQLContext携带当前用户解析器通过DataFetchingEnvironment获取//The way to inject the context is via DataFetchingEnvironment public Link createLink(String url, String description, DataFetchingEnvironment env) { AuthContext context env.getContext(); Link newLink new Link(url, description, context.getUser().getId()); linkRepository.saveLink(newLink); return newLink; }GraphQLEndpoint则通过重写createContext方法从 HTTP 请求的Authorization头解析出用户并放入 context——这也再次说明mutation 解析器可以额外接收DataFetchingEnvironment参数GraphQL 执行引擎会自动注入从而在不污染 SDL 的前提下访问请求级上下文。关于本教程的时效性说明需要特别说明的是教程作者在 Introduction 章节 中明确警告本 Java 教程已过时且使用了一些第三方库graphql-java-tools、graphql-java-servlet并未清楚说明这些并非 graphql-java 核心本身仓库 README 也把 graphql-java 教程标注为 Out of date。因此本文的代码示例与依赖版本如graphql-java 3.0.0、graphql-java-tools 3.2.0均为教程撰写时的快照用于理解概念与模式完全合适但不宜直接照搬到新项目学习时请把重点放在SDL 定义 → 根解析器 → SchemaParser 接线 → GraphiQL 验证这一稳定的方法论上具体库 API 请以当前官方文档为准。小结围绕createLink这一最小可用示例本文完整覆盖了 Java 服务端 mutation 的实现链路在schema.graphqls中定义 mutation 根字段与参数 → 在schema块声明mutation入口 → 编写实现GraphQLRootResolver的Mutation类方法形参与 SDL 参数一一对应→ 在GraphQLEndpoint#buildSchema中用SchemaParser.resolvers(...)注册 → 重启 Jetty 后在 GraphiQL 中执行并配合allLinks验证落库。在此基础上mutation 还可以扩展出input类型参数、自定义标量、关联对象解析与 context 注入等高级用法配合 More Mutations、Authentication 与 Connectors 等相邻章节即可逐步构建出接近真实 Hackernews 的完整后端。赞分享【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载相关推荐Microdiff 与主流差异库对比deep-diff、deep-object-diff 和 jsDiff 性能分析Microdiff 与主流差异库对比deep diff、deep object diff 和 jsDiff 性能分析 在现代 JavaScript 开发中对使用 graphql-java 在 HackerNews GraphQL 服务中实现服务端 limit-offset 分页使用 graphql java 在 HackerNews GraphQL 服务中实现服务端 limit offset 分页 导读 本篇文章基于 howtograGraphQL Java Tools: 简化GraphQL在Java中的开发与应用GraphQL Java Tools: 简化GraphQL在Java中的开发与应用 在现代Web开发中 GraphQL https://graphql.org上一篇Dozzle 前置代理认证Forward Proxy完全指南接入 Authelia、Cloudflare Zero Trust 与 oauth2-proxy/Pocket ID下一篇fuzzy.js实战教程在React/Vue项目中集成模糊搜索功能的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表