API查询语言GraphQL(二)SpringBoot整合Graphql问题拓展

本文探讨了在微服务架构下,如何利用GraphQL进行高效的数据查询。介绍了如何定义和管理多个实体类的查询,以及如何实现动态参数查询,提高查询灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题一.有多个不同实体类被查询和返回

  • 因为一个微服务通常只会定义一个.graphqls文件 所以这个文件会包含要通过graphql查询的所有不同实体类
schema { 
	query: UserQuery 
}

type UserQuery { 
	#查询实体类一:User,对应查询名称也为User
	User(id:Long) : User
	#查询实体类二:TableResult,对应查询名称也为TableResult
    UsersList(page:Int, pageSize:Int) : TableResult
    #查询实体类三:IndexAdResult ,对应查询名称也为IndexAdResult 
    IndexAdList:IndexAdResult 
}

#对应实体类User
type User { 
	id:Long! 
	name:String 
	age:Int 
}

#对应实体类TableResult
type TableResult{
    list:[User]
    pagination:Pagination
}

#对应实体类Pagination
type Pagination{
    current:Int
    pageSize:Int
    total:Int
}

#对应实体类IndexAdResult
type IndexAdResult{
	# 查询类型IndexAdResult的属性为类型IndexAdResultData的集合
	# 因为字段名称list对应的类型是自定类型 所以还要在其后展开 查询条件为:
	# {
    #	IndexAdList{
    #    	list{
    #        	original
    #    	 }
    #	}
	# }
	#
    list:[IndexAdResultData]
}

#对应实体类IndexAdResultData
type IndexAdResultData{
    original:String
}
  • 存在弊端:DataFetcher增多 不好管理
  • 解决方案:定义一个DataFetcher的接口 所有DataFetcher都实现该接口 然后所有该DataFetcher的实现类 都注入到GraphqlProvider中

1、MyDataFetcher(接口)

public interface MyDataFetcher {
	//查询类型中实体类对应的查询名称
    String fieldName();
	
	//通过DataFetchingEnvironment 获取查询条件对象的参数值 并返回查询到的数据
    Object dataFetcher(DataFetchingEnvironment environment);
}

2、UserDataFetcher(MyDataFetcher实现类 对应UserQuery中User对象)

//必须注入spring 才可以在spring扫描MyDataFetcher的子类时扫描到
@Component
public class UserDataFetcher implements MyDataFetcher {

    @Autowired
    private UserService userService;

    @Override
    public String fieldName() {
    	//返回查询名称User 然后可以通过该名称找到对应的类型
        return "User";
    }

    @Override
    public Object dataFetcher(DataFetchingEnvironment environment) {
    	//获取User查询对象的参数id
        Long id = environment.getArgument("id");
        return this.userService.queryUserById(id);
    }
}

3、UserListDataFetcher(MyDataFetcher实现类 对应UserQuery中UsersList对象)

//必须注入spring 才可以在spring扫描MyDataFetcher的子类时扫描到
@Component
public class UserListDataFetcher implements MyDataFetcher {

    @Autowired
    private UserService userService;

    @Override
    public String fieldName() {
   	    //返回查询名称UsersList 然后可以通过该名称找到对应的类型
        return "UsersList";
    }

    @Override
    public Object dataFetcher(DataFetchingEnvironment environment) {
    	//获取UsersList查询对象的参数page
        Integer page = environment.getArgument("page");
        if(null == page){
            page = 1;
        }
        //获取UsersList查询对象的参数pageSize
        Integer pageSize = environment.getArgument("pageSize");
        if(null == pageSize){
            pageSize = 5;
        }

        return this.userService.queryList(null, page, pageSize);
    }
}

4、此处省略IndexAdList实体类的DataFetcher
5、GraphqlProvider(重新实现buildWiring方法)

@Component
public class GraphqlProvider {

    private GraphQL graphQL;

	//注入MyDataFetcher的所有实现类
    @Autowired
    private List<MyDataFetcher> myDataFetchers;

    @Bean
    private GraphQL graphQL(){
        return this.graphQL;
    }

    //实现对GraphQL对象的初始化
    @PostConstruct
    public void init() throws FileNotFoundException {
        File file = ResourceUtils.getFile("classpath:user.graphqls");
        this.graphQL = GraphQL.newGraphQL(buildGraphQLSchema(file)).build();

    }

    private GraphQLSchema buildGraphQLSchema(File file) {
        TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(file);
        return new SchemaGenerator().makeExecutableSchema(typeRegistry, buildWiring());
    }

    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                .type("UserQuery", builder -> {
                			//遍历myDataFetcher的所有实现类
                            for (MyDataFetcher myDataFetcher : myDataFetchers) {
                            	//获取查询的名称 然后找到其对应的类型
                                builder.dataFetcher(myDataFetcher.fieldName(),
                                		//调用不同的myDataFetcher实现类的dataFetcher方法 返回查询到的数据
                                        environment -> myDataFetcher.dataFetcher(environment));
                            }
                            return builder;
                        }

                )
                .build();
    }


}

6.使用方法和上一篇的controller相同

问题二.GraphQL如何不将参数写死

  • GraphQL的支持动态参数
    1.语法
  1. 传统静态参数查询语法
    {
        实体类查询名(字段名:"参数值"){
            查询字段1
            查询字段2
        }
    }
    
  2. 动态参数查询写语法
    • QUERY
      query 实体类查询名($参数名:类型){
          实体类查询名(字段名:$参数名){
              查询字段1
              查询字段2
          }
      }
      
    • GRAPHQL VARIABLES
      {
      	"参数名":参数值
      }
      

3.动态参数写法示例(查询上面的UsersList)
在这里插入图片描述

  • 说明:
    • operationName-> 表示操作名称,这个随意。
    • $pageSize: Int-> 定义参数名以及参数类型
    • (pageSize: $pageSize) -> 通过参数名使用参数的值

4.SpringBoot如何接收使用

  • 不用改动任何Provider和DataFetcher 只用改动Controller 解析前端传来数据就可以
  • 静态参数的时候 json字符串只有一个字段query:{query:"…"}
  • 静态参数的时候 json字符串只有多个字段:{query:"…",varivaables:{},operationName:"…"}
    • query:查询语句
    • varivaables:参数的map
    • operationName:就是query operationName{ },在query之后跟的那个名字
  • 注意:动态参数的variables对应的是参数和值map 不是string字符串
@Controller
@RequestMapping("graphql")
public class GraphQLController {

    @Autowired
    //还是原先那个provider注入的graphQL
    private GraphQL graphQL;

	//解析前端前端传来的json字符串
    private static ObjectMapper MAPPER=new ObjectMapper();

    @GetMapping
    @ResponseBody
	/**
	 *	原先静态参数的时候json字符串只有一个字段 所以转成Map和String差不多 但是动态参数友三个字段 转成Map更方便
	 */
    //让SpringBoot把json字符串转化为Map类型
    public Map<String,Object> getQuery(@RequestBody Map<String,Object> params) throws IOException {
        return parseQuery(params);
    }

    @PostMapping
    @ResponseBody
    //让SpringBoot把json字符串转化为Map类型
    public Map<String,Object> postQuery(@RequestBody Map<String,Object> params) throws IOException {
        return parseQuery(params);
    }

	//解析前端json字符串转化后的map对象
    public Map<String,Object> parseQuery(Map<String,Object> params) throws IOException {
    	//获取查询语句
        String query= (String) params.get("query");
        if(StringUtils.isBlank(query)){
            Map<String, Object> error = new HashMap<>();
            error.put("status", 500);
            error.put("msg", "查询出错");
            return error;
        }

		//获取操作名
        String operationName= (String) params.get("operationName");
        //获取变量的map
        Map<String ,Object> variables= (Map<String, Object>) params.get("variables");

		//将query、operationName、variables构建成一个查询语句
        ExecutionInput executionInput=ExecutionInput.newExecutionInput()
                .query(query)
                .operationName(operationName)
                .variables(variables)
                .build();

        return graphQL.execute(executionInput).toSpecification();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值