这次来说一下相对应 “增删改的” Mutation操作,也是使用对应的DataFetcher,只不过增加了一个增删改的操作后返回查询结果罢了,老样子来看一个例子,IDL方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | schema { mutation: userMutation query: userQuery } type userQuery { user(id: Int): [User] } type userMutation { createUser(addUser:UserInput): [User] } input UserInput{ id: Int age: Int userName: String! dogs: [DogInput] } input DogInput { id: Int dogName: String! } type User { id: Int age: Int userName: String! dogs(dogId:Int): [Dog] } type Dog { id: Int dogName: String! } |
所有的入参除了基础标量类型以外,需要使用input类型来指代自定义类型,同时在定义Mutation的同时,必须有1个Query操作,不然执行Schema校验的时候会报错,不知道是什么原因,不过项目中肯定是Mutation与Query共存的,也就不深究了。另外大家看到在新增操作的地方,参数我使用了一个自定义input对象。如果是基础类型的话,方式与Query一直;但如果使用了自定义类型,在进行Variable设置的时候是有一定区别的,具体看Java代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | //构建一个运行时Java语义 绑定schema,包括(datafetcher、typeResolver、customScalar) private static RuntimeWiring buildRuntimeWiring() { //return RuntimeWiring.newRuntimeWiring().wiringFactory(new EchoingWiringFactory()).build(); return RuntimeWiring.newRuntimeWiring() // this uses builder function lambda syntax .type("userMutation", typeWiring -> typeWiring .dataFetcher("createUser", environment -> { //上一级对象数据 environment.getSource() //Map<String,Object> environment.getArguments() //环境上下文,整个查询冒泡中都可以使用 environment.getContext() //只能获取到LinkedHashMap,无法直接转换成对象 Map<String,Objects> paramMap = environment.getArgument("addUser"); System.out.println("argument:paramMap=" + paramMap); // repository 处理 return getUsers(123); }) ).type("userQuery", typeWiring -> typeWiring .dataFetcher("user", environment -> { //上一级对象数据 environment.getSource() //Map<String,Object> environment.getArguments() //环境上下文,整个查询冒泡中都可以使用 environment.getContext() Integer id = environment.getArgument("id"); System.out.println("argument:id=" + id); // repository 处理 return getUsers(id); }) ).type("User", typeWiring -> typeWiring .dataFetcher("dogs", environment -> { //获取父对象 User user = environment.getSource(); int userId = user.getId(); System.out.println("dogs outside userId = " + userId); Integer paramDogId = environment.getArgument("dogId"); System.out.println("dogs inside dogId = " + paramDogId); //模拟rpc调用 List<Dog> dogs = Lists.newArrayList(); return dogs; })) .build(); } //schema public static void mainExec() throws InterruptedException { //创建Schema SchemaParser schemaParser = new SchemaParser(); SchemaGenerator schemaGenerator = new SchemaGenerator(); File schemaFile = loadSchema("graphql/userMutation.graphqls"); TypeDefinitionRegistry typeRegistry = schemaParser.parse(schemaFile); RuntimeWiring wiring = buildRuntimeWiring(); GraphQLSchema schema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring); //测试输出 GraphQL graphQL = GraphQL.newGraphQL(schema).build(); Map<String,Object> variable = Maps.newHashMap(); variable.put("uuu",MutationVariableHandler.getVariablesMapFromString(JSON.toJSONString(getUsers(123).get(0)))); //变量名不要和类型名一样,不然好像有点问题 ExecutionInput executionInput = ExecutionInput.newExecutionInput(). variables(variable). query("mutation userMutation($uuu:UserInput){createUser(addUser:$uuu)" + "{id,age,dogs{id,dogName}}}").build(); ExecutionResult result = graphQL.execute(executionInput); Map<String, Object> data = result.getData(); List errors = result.getErrors(); System.out.println(data); System.out.println("errors = "+errors); } |
Console:
argument:paramMap={id=1, age=0, userName=bf2e46b6-dcf3-47e2-a14c-a305e38d3926, dogs=[{id=100, dogName=Dog11e0e22c-0b44-4018-968f-d655e64d4260}]}
dogs outside userId = 1
dogs inside dogId = null
{createUser=[{id=1, age=7, dogs=[]}]}
uuu是我自己定义的类型为UserInput的Variable,在成功执行之前,我试过在map的value里放过json字符串、user对象,但GraphQL引擎执行的时候,会报如下错误:
graphql.schema.CoercingParseValueException: Variable ‘uuu’ has an invalid value. Expected type ‘Map’ but was ‘User’. Variables for input objects must be an instance of type ‘Map’.
看了下源码,发现在使用Variable进行query执行的时候,自定义类型必须以Map<String,Object>的形式入参,这就很尴尬了,贴上现在的转换方式,比较傻。但如果集合Web项目的话,入参可以直接转换成Map,应该会好一点:
1 2 3 4 5 6 7 8 9 10 | private static ObjectMapper jacksonObjectMapper = new ObjectMapper(); private static TypeReference<HashMap<String, Object>> typeRefReadJsonString = new TypeReference<HashMap<String, Object>>() { }; public static Map<String, Object> getVariablesMapFromString(String variablesFromRequest) { try { return jacksonObjectMapper.readValue(variablesFromRequest, typeRefReadJsonString); } catch (IOException exception) { throw new GraphQLException("Cannot parse variables", exception); } } |
说实话,写到这边个人认为Mutation只是单纯一个语义标识的操作类型,其实在Query的DataFetcher中也可以做插入和修改操作;而且Mutation同时也需要返回结果,和Query也类似。