Spring for GraphQL:不修改Schema如何避免数据过度/不足获取?
问题描述
我正在使用Spring for GraphQL,希望让GraphQL解析器避免数据过度获取与不足获取,但不想修改Schema以匹配数据库的数据分组方式。我了解过N+1问题,但本次场景有所不同。
现有GraphQL Schema如下:
type Query { employee(name: String, companyName: String): Employee } type Employee { name: String companyName: String age: Int # 来源:Person表 address: String # 来源:Person表 jobTitle: String # 来源:Employee表 startDate: String # 来源:Employee表 }
请问是否可以配置解析器,让age和address始终一起获取,jobTitle和startDate始终一起获取?
修改Schema将这些字段分组会导致与数据库紧耦合,比如:
type Employee { name: String companyName: String personalDetails : PersonalDetails # 来源:Person表 jobDetails: JobDetails # 来源:Employee表 } type PersonalDetails { age: Int address: String } type JobDetails { jobTitle: String startDate: String }
目前示例代码使用四个解析器填充Employee数据,我希望仅使用两个:
@Controller public class GraphqlController { record Employee(String name, String companyName, Integer age, String address, String jobTitle, String startDate) { } @QueryMapping public Employee employee(@Argument String name, @Argument String companyName) { return new Employee(name, companyName, null, null, null, null); } @SchemaMapping public Integer age(Employee employee) { return 35; } @SchemaMapping public String address(Employee employee) { return "US"; } @SchemaMapping public String jobTitle(Employee employee) { return "Software"; } @SchemaMapping public String startDate(Employee employee) { return "Monday"; } }
解决方案
完全可以实现你的需求,不需要修改现有Schema,只需要通过Spring GraphQL的解析器关联机制,将同属一个数据库表的字段绑定到同一个数据获取逻辑中。具体做法如下:
核心思路
创建两个辅助数据类(对应数据库的两个表分组),编写两个核心解析器方法分别获取这两组数据,再将原Schema中的单个字段映射到对应辅助类的属性上。这样无论客户端请求age还是address,都会触发一次完整的Person表查询;请求jobTitle或startDate时,触发一次完整的Employee表查询,避免重复查询或数据拆分获取。
代码实现
@Controller public class GraphqlController { // 仅保留Schema顶层返回的核心字段,无需包含后续要填充的关联字段 record Employee(String name, String companyName) {} // 对应Person表的分组数据,仅作为后端数据载体 record PersonalDetails(Integer age, String address) {} // 对应Employee表的分组数据,仅作为后端数据载体 record JobDetails(String jobTitle, String startDate) {} @QueryMapping public Employee employee(@Argument String name, @Argument String companyName) { // 仅返回基础字段,关联字段交给后续解析器处理 return new Employee(name, companyName); } // 一次性获取Person表的age和address数据 @SchemaMapping public PersonalDetails personalDetails(Employee employee) { // 此处替换为实际数据库查询逻辑,一次查询获取两个字段 return new PersonalDetails(35, "US"); } // 将age字段映射到PersonalDetails的age属性 @SchemaMapping(field = "age") public Integer age(PersonalDetails personalDetails) { return personalDetails.age(); } // 将address字段映射到PersonalDetails的address属性 @SchemaMapping(field = "address") public String address(PersonalDetails personalDetails) { return personalDetails.address(); } // 一次性获取Employee表的jobTitle和startDate数据 @SchemaMapping public JobDetails jobDetails(Employee employee) { // 此处替换为实际数据库查询逻辑,一次查询获取两个字段 return new JobDetails("Software", "Monday"); } // 将jobTitle字段映射到JobDetails的jobTitle属性 @SchemaMapping(field = "jobTitle") public String jobTitle(JobDetails jobDetails) { return jobDetails.jobTitle(); } // 将startDate字段映射到JobDetails的startDate属性 @SchemaMapping(field = "startDate") public String startDate(JobDetails jobDetails) { return jobDetails.startDate(); } }
原理说明
- Spring GraphQL会根据字段依赖关系自动解析:当客户端请求
age或address时,会先调用personalDetails方法获取完整分组数据,再提取对应字段值,确保只执行一次数据库查询。 - 辅助类仅作为后端数据分组的载体,不会暴露到Schema中,完全避免了Schema与数据库结构的耦合。
- 最终仅用两个核心数据获取逻辑(
personalDetails和jobDetails)处理所有关联字段,满足你的需求。
内容的提问来源于stack exchange,提问作者neets
相关产品推荐
相关产品推荐

