问题一.有多个不同实体类被查询和返回
- 因为一个微服务通常只会定义一个.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 查询字段2 } }
- 动态参数查询写语法
- QUERY
query 实体类查询名($参数名:类型){ 实体类查询名(字段名:$参数名){ 查询字段1 查询字段2 } }
- GRAPHQL VARIABLES
{ "参数名":参数值 }
- QUERY
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();
}
}