You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Spring GraphQL查询编写Resolver并按实例名称过滤客户

Spring GraphQL Resolver 实现:按实例名称过滤客户

核心思路

要解决跨API数据组合及避免N+1调用问题,核心是提前按过滤条件串联查询:先通过实例名称找到对应Instance,再查询该实例下的所有Tenant,最后获取关联的Customer,一次性完成数据组装,避免GraphQL自动触发子字段的重复API调用。

步骤1:定义数据模型

对应GraphQL Schema,创建Java实体类(示例用Lombok简化代码):

// Instance.java
@Data
public class Instance {
    private String instanceId;
    private String instanceName;
}

// Tenant.java
@Data
public class Tenant {
    private String tenantId;
    private Instance instance;
}

// Customer.java
@Data
public class Customer {
    private String customerName;
    private List<Tenant> tenants;
}

// 输入参数类
@Data
public class CustomerFilterInput {
    private String customerName;
    private TenantInput tenant;
}

@Data
public class TenantInput {
    private String tenantId;
    private InstanceInput instance;
}

@Data
public class InstanceInput {
    private String instanceName;
    private String instanceId;
}

步骤2:封装外部API调用

模拟不同API的客户端(实际项目中可替换为RestTemplate、WebClient或Feign接口):

@Component
public class InstanceApiClient {
    // 调用外部Instance API,根据名称查询实例
    public Instance getInstanceByName(String instanceName) {
        Instance instance = new Instance();
        instance.setInstanceId("INST1");
        instance.setInstanceName(instanceName);
        return instance;
    }
}

@Component
public class TenantApiClient {
    // 调用外部Tenant API,根据实例ID查询租户列表
    public List<Tenant> getTenantsByInstanceId(String instanceId) {
        Instance instance = new Instance();
        instance.setInstanceId(instanceId);
        instance.setInstanceName("ABC");

        Tenant tenant1 = new Tenant();
        tenant1.setTenantId("TEN1");
        tenant1.setInstance(instance);

        Tenant tenant2 = new Tenant();
        tenant2.setTenantId("TEN2");
        tenant2.setInstance(instance);

        return Arrays.asList(tenant1, tenant2);
    }
}

@Component
public class CustomerApiClient {
    // 调用外部Customer API,根据租户ID列表查询客户
    public List<Customer> getCustomersByTenantIds(List<String> tenantIds) {
        Customer customer1 = new Customer();
        customer1.setCustomerName("Customer A");
        customer1.setTenants(new ArrayList<>());

        Customer customer2 = new Customer();
        customer2.setCustomerName("Customer B");
        customer2.setTenants(new ArrayList<>());

        return Arrays.asList(customer1, customer2);
    }
}

步骤3:编写Query Resolver

实现GraphQLQueryResolver,在顶层查询中完成所有数据的过滤与组装:

@Component
public class CustomerQueryResolver implements GraphQLQueryResolver {
    private final InstanceApiClient instanceApiClient;
    private final TenantApiClient tenantApiClient;
    private final CustomerApiClient customerApiClient;

    // 构造方法注入依赖
    public CustomerQueryResolver(InstanceApiClient instanceApiClient,
                                 TenantApiClient tenantApiClient,
                                 CustomerApiClient customerApiClient) {
        this.instanceApiClient = instanceApiClient;
        this.tenantApiClient = tenantApiClient;
        this.customerApiClient = customerApiClient;
    }

    public List<Customer> customers(CustomerFilterInput filter) {
        // 校验过滤条件是否存在
        if (filter == null || filter.getTenant() == null || filter.getTenant().getInstance() == null) {
            return Collections.emptyList();
        }

        String targetInstanceName = filter.getTenant().getInstance().getInstanceName();

        // 1. 查询目标实例
        Instance targetInstance = instanceApiClient.getInstanceByName(targetInstanceName);
        if (targetInstance == null) {
            return Collections.emptyList();
        }

        // 2. 查询该实例下的所有租户
        List<Tenant> matchedTenants = tenantApiClient.getTenantsByInstanceId(targetInstance.getInstanceId());
        if (matchedTenants.isEmpty()) {
            return Collections.emptyList();
        }

        // 提取租户ID列表
        List<String> tenantIds = matchedTenants.stream()
                .map(Tenant::getTenantId)
                .collect(Collectors.toList());

        // 3. 查询关联的客户列表
        List<Customer> matchedCustomers = customerApiClient.getCustomersByTenantIds(tenantIds);

        // 4. 组装客户与租户的关联关系
        // 注:此处逻辑需根据实际业务调整,示例为每个客户关联所有匹配的租户
        matchedCustomers.forEach(customer -> customer.setTenants(matchedTenants));

        // 若需仅保留客户中属于目标实例的租户,可替换为以下逻辑:
        // matchedCustomers.forEach(customer -> {
        //     List<Tenant> filteredTenants = customer.getTenants().stream()
        //             .filter(t -> t.getInstance().getInstanceId().equals(targetInstance.getInstanceId()))
        //             .collect(Collectors.toList());
        //     customer.setTenants(filteredTenants);
        // });

        return matchedCustomers;
    }
}

关键注意事项

  • 避免N+1调用:通过串联查询提前获取所有关联数据,而非先查客户再逐个拉取租户和实例,彻底杜绝GraphQL自动触发的子字段API调用。
  • 过滤逻辑灵活调整:可根据业务需求,选择返回包含目标实例租户的客户(保留所有租户),或仅返回客户中属于目标实例的租户。
  • 依赖注入:通过Spring的依赖注入管理API客户端,确保代码可维护性与扩展性。

内容的提问来源于stack exchange,提问作者Ajay Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:20:27