精华内容
下载资源
问答
  • 3分钟集成GraphQL GraphQL是一个从服务端检数据的查询语言。某种程度上,是REST、SOAP、或者gRPC的替代品。 假设我们要从在线商店的后台查询一本特定书记的详细信息。 使用GraphQL,你可以发送以下的查询到服务端来...

    3分钟集成GraphQL

    GraphQL是一个从服务端检数据的查询语言。某种程度上,是REST、SOAP、或者gRPC的替代品。

    假设我们要从在线商店的后台查询一本特定书记的详细信息。

    使用GraphQL,你可以发送以下的查询到服务端来获取id为“book-1”的书籍的详细信息。

    {
      bookById(id: "book-1"){
        id
        name
        pageCount
        author {
          firstName
          lastName
        }
      }
    }
    

    这不是JSON,虽然看起来很像。他是一个GraphQL查询语句。语句的含义是:

    • 查询指定id的一本书
    • 返回书籍的id、name、pageCount、author
    • 对于author字段,还需要包含firstName和lastName

    返回结果是一个正常的JSON格式:

    {
      "bookById":
      {
        "id":"book-1",
        "name":"Harry Potter and the Philosopher's Stone",
        "pageCount":223,
        "author": {
          "firstName":"Joanne",
          "lastName":"Rowling"
        }
      }
    }
    

    GraphQL一个非常重要的特性–它是静态类型的:服务端明确的知道你可以查询的每个对象的类型,客户端可以从服务端获取“schema”。Schema描述了哪些查询是可能的,哪些字段能被获取到。(注意:我们这里说的Schema指的是GraphQL Schema,他与其他Schema比如JSON Schema获取DataBase Schema无关)

    上面查询的Schema如下所示:

    type Query {
      bookById(id: ID): Book
    }
    
    type Book {
      id: ID
      name: String
      pageCount: Int
      author: Author
    }
    
    type Author {
      id: ID
      firstName: String
      lastName: String
    }
    

    这个教程将重点介绍如何使用Schema在java中实现一个GraphQL服务。

    GraphQL Java概述

    GraphQL Java是GraphQL的Java服务端的实现。在GraphQL Java Github org上有几个仓库。最重要的一个是GraphQL Java Engine(https://github.com/graphql-java/graphql-java),这是一切的基础。

    GraphQL Java Engine只关心查询的执行。它不处理任何HTTP或者JSON相关的事。基于这些方面,我们将用GraphQL Java Spring Boot适配器,它负责通过SpringBoot基于HTTP暴露我们的API。

    创建一个GraphQL Java服务主要的步骤:

    1. 定义GraphQL Schema
    2. 决定一个查询如何获取到数据

    示例API:获取书的详情

    我们的示例项目将会写一个简单的接口:获取指定书记的详细信息。虽然这不是一个全面的API,但对于本教程来说足够了。

    创建一个SpringBoot项目

    导入3个包:

            <dependency>
                <groupId>com.graphql-java</groupId>
                <artifactId>graphql-java</artifactId>
                <version>17.3</version>
            </dependency>
            <dependency>
                <groupId>com.graphql-java</groupId>
                <artifactId>graphql-java-spring-boot-starter-webmvc</artifactId>
                <version>2.0</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>31.0.1-jre</version>
            </dependency>
    

    前2个是GraphQL Java和GraphQL Java Spring。Guava不是必要的,但是它能让我们更方便点。

    Schema

    在src/main/resources下面创建一个新文件schema.graphqls,写入一下内容:

    type Query {
      bookById(id: ID): Book
    }
    
    type Book {
      id: ID
      name: String
      pageCount: Int
      author: Author
    }
    
    type Author {
      id: ID
      firstName: String
      lastName: String
    }
    

    这个Schema定义了一个顶级字段(在Query类型中):bookById,它返回指定熟记的详情。

    它也定义了类型Book,包含字段:id,name,pageCount,author。author的类型是Author,这个在Book类型之后也定义了。

    上面用来描述Schema的专用语言叫做Schema Definition Language 或者叫DSL。更多细节可以查看:https://graphql.org/learn/schema/

    这个文件完成功后,我们就得使用它。

    我们创建一个新的类GraphQLProvider,定义一个init方法,这个方法用来创建GraphQL实例:

    @Component
    public class GraphQLProvider {
    
        private GraphQL graphQL;
    
        @Bean
        public GraphQL graphQL() {
            return graphQL;
        }
    
        @PostConstruct
        public void init() throws IOException {
            URL url = Resources.getResource("schema.graphqls");
            String sdl = Resources.toString(url, Charsets.UTF_8);
            GraphQLSchema graphQLSchema = buildSchema(sdl);
            this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
        }
    
        private GraphQLSchema buildSchema(String sdl) {
          // TODO: 我们稍后将在这创建Schema
        }
    }
    

    我们使用Guava的Resources去读取之前创建的文件。接下来创建GraphQLSchema和GraphQL实例。这个GraphQL实例通过带有@Bean注解的graphQL()方法作为一个Spring Bean被暴露。GraphQL Java Spring适配器将使用GraphQL实例让我们的schema生效,通过HTTP,默认url: /graphql。

    我们还需要做的就是实现buildSchema方法,该方法创建GraphQLSchema实例,连接sdl和DataFetcher。

    @Autowired
    GraphQLDataFetchers graphQLDataFetchers;
    
    private GraphQLSchema buildSchema(String sdl) {
        TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
        RuntimeWiring runtimeWiring = buildWiring();
        SchemaGenerator schemaGenerator = new SchemaGenerator();
        return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
    }
    
    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                .type(newTypeWiring("Query")
                        .dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
                .type(newTypeWiring("Book")
                        .dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
                .build();
    }
    

    TypeDefinitionRegistry是schema文件的解析版本。SchemaGenerator将TypeDefinitionRegistry和RuntimeWiring结合起来,生成GraphQLSchema。

    buildWiring方法使用graphQLDataFetchers bean去注册2个DataFetcher

    • 一个是通过指定id检索book
    • 一个是获取指定book的author

    DataFetcher和如何实现GraphQLDataFetchers bean在下部分内容说明。

    创建GraphQLGraphQLSchema实例总体过程如下所示:
    创建过程

    DataFetchers

    也许GraphQL Java server最重要的概念就是DataFetcher:在查询执行的时候,一个DataFetcher获取一个字段的数据。

    当GraphQL Java在执行一个查询时,它会为查询语句中的每个字段调用合适的DataFetcher。一个DataFetcher是一个接口,其中只有一个方法,发发只有一个DataFetcherEnvironment参数:

    public interface DataFetcher<T> {
        T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
    }
    

    重点:schema中的每个字段都有一个DataFethcer与之关联。如果你没有给某个字段定义一个DataFetcher,那么会使用默认的PropertyDataFetcher。我们稍后会消息讨论这点。

    我们接下来创建一个新类GraphQLDataFetchers,它包含books和authors的样例list。

    完整的实现如下所示:

    @Component
    public class GraphQLDataFetchers {
    
        private static List<Map<String, String>> books = Arrays.asList(
                ImmutableMap.of("id", "book-1",
                        "name", "Harry Potter and the Philosopher's Stone",
                        "pageCount", "223",
                        "authorId", "author-1"),
                ImmutableMap.of("id", "book-2",
                        "name", "Moby Dick",
                        "pageCount", "635",
                        "authorId", "author-2"),
                ImmutableMap.of("id", "book-3",
                        "name", "Interview with the vampire",
                        "pageCount", "371",
                        "authorId", "author-3")
        );
    
        private static List<Map<String, String>> authors = Arrays.asList(
                ImmutableMap.of("id", "author-1",
                        "firstName", "Joanne",
                        "lastName", "Rowling"),
                ImmutableMap.of("id", "author-2",
                        "firstName", "Herman",
                        "lastName", "Melville"),
                ImmutableMap.of("id", "author-3",
                        "firstName", "Anne",
                        "lastName", "Rice")
        );
    
        public DataFetcher getBookByIdDataFetcher() {
            return dataFetchingEnvironment -> {
                String bookId = dataFetchingEnvironment.getArgument("id");
                return books
                        .stream()
                        .filter(book -> book.get("id").equals(bookId))
                        .findFirst()
                        .orElse(null);
            };
        }
    
        public DataFetcher getAuthorDataFetcher() {
            return dataFetchingEnvironment -> {
                Map<String,String> book = dataFetchingEnvironment.getSource();
                String authorId = book.get("authorId");
                return authors
                        .stream()
                        .filter(author -> author.get("id").equals(authorId))
                        .findFirst()
                        .orElse(null);
            };
        }
    }
    

    数据源

    我们从这个类中的静态列表里获取books和authors,这会帮助你理解GraphQL对数据来源没有任何要求。这是GraphQL的优势:数据可以来自内存里的静态数据,来自数据库或者外部服务。

    Book DataFetcher

    我们第一个方法getBookByIdDataFetcher返回一个DataFetcher的实现–使用DataFetcherEnvironment返回指定的book。这个例子的意思是,我们需要从bookById字段拿到id参数,然后找到这个id对应的book。如果没有找到,返回null。

    String bookId = dataFetchingEnvironment.getArgument("id");中的“id”指的是在schema中的query字段bookById

    type Query {
      bookById(id: ID): Book
    }
    

    Author DataFetcher

    第二个方法getAuthorDataFetcher,返回一个DataFetcher的实现–获取指定book的author。和book的DataFetcher方法比较,这个我们没有获取参数,但是我们有一个book的实例。通过getSource方法可以获取到有效的父类字段,然后得到DataFetcher结果。这是一个重要的概念:GraphQL中每个字段的DataFetcher都是以自顶向下的方式被调用,父类的结果是子类DataFetcherEnvironmentsource属性。

    我们接下来使用前面获取的book拿到authorId,然后使用和查询书相同的方式查询指定的author。

    Default DataFetchers

    我们只实现了2个DataFetchers。正如前面提及的,如果你没有实现,那么就会使用默认的PropertyDataFetcher。按我们的例子来讲,意思就是说Book.id,Book.name,Book.pageCount,Author.id,Author.firstname,Author.lastName都有一个默认的PropertyDataFetcher分别与之关联。

    PropertyDataFetcher通过多种方式尝试在Java对象上获取属性。例子中java.util.Map很容易通过key查找属性。这对我们来说很好,因为book和Author的Maps的keys和schema中字段是一模一样的。比如,schema中我们定义Book类型的字段pageCount,book的DataFetcher返回的Map中有key pageCount. 因为字段名字和Map中的key一样,所以PropertyDateFetcher才能起作用。

    让我们假设一下,我们有一个字段不匹配,book Map中的key是totalPages,而不是pageCount。这将会导致所有book的pageCount值为null,因为PropertyDataFetcher无法获取到正确的值。为了解决这个问题,你需要为Book.pageCount去注册一个新的DataFetcher,所下所示:

    // In the GraphQLProvider class
    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                .type(newTypeWiring("Query")
                        .dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
                .type(newTypeWiring("Book")
                        .dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher())
                        // This line is new: we need to register the additional DataFetcher
                        .dataFetcher("pageCount", graphQLDataFetchers.getPageCountDataFetcher()))
                .build();
    }
    
    // In the GraphQLDataFetchers class
    // Implement the DataFetcher
    public DataFetcher getPageCountDataFetcher() {
        return dataFetchingEnvironment -> {
            Map<String,String> book = dataFetchingEnvironment.getSource();
            return book.get("totalPages");
        };
    }
    

    试用API

    这就是构建GraphQL API所需要的全部内容。在运行SpringBoot项目后,使用http://localhost:8080/graphql地址下能看到API了。

    尝试和查看GraphQL API最简单的方式是使用GraphQL Playground(https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/)这样的工具。下载并运行。

    启动之后你会要求输入一个URL,输入:http://localhost:8080/graphql

    之后,你就可以查询我们之前写的样例API。

    graphql-java-kickstart

    上面所讲的,引用了2个jar包,graphql-java是GraphQL的java实现形式。graphql-java-spring-boot-starter-webmvc是为了支持SpringWeb方式请求。

    而graphql-java-kickstart是在这之上,不仅包含graphql-java,还有graphql-java-tools、graphql-java-servlet。支持了图形界面,我们只需要引入这一个依赖就可以了。

            <dependency>
                <groupId>com.graphql-java-kickstart</groupId>
                <artifactId>graphql-spring-boot-starter</artifactId>
                <version>12.0.0</version>
            </dependency>
    

    开启GraphIQL和PlayGround

    这两个都是graphql语法执行图形界面。

    开启方式如下:

    graphql:
        graphiql:
            enabled: true
        playground:
            enabled: true
    

    创建schema

    和之前所讲一样,需要创建schema文件,后缀必须是.graphqls

    type Query {
      bookById(id: String): Book
    }
    
    type Book {
      id: String
      name: String
      pageCount: Int
      author: Author
    }
    
    type Author {
      id: String
      firstName: String
      lastName: String
    }
    
    

    创建对应的Object

    Schema中定义的每一个数据都需要有一个对应的Java对象:

    @Data
    class Book{
        private String id;
        private String name;
        private Integer pageCount;
        private Author author;
    }
    
    @Data
    class Author{
        private String id;
        private String firstName;
        private String lastName;
    }
    

    创建Resolver

    和之前不一样的是,这个不需要我们写详细的解析步骤,只需要写一个Resolver。

    @Component
    public class BookResolver implements GraphQLQueryResolver {
    
        public Book bookById(String id){
            Book book = new Book();
            book.setId("book1");
            book.setName("test");
            return book;
        }
    }
    

    @Component注解是为了被Spring发现。GraphQLQueryResolver接口是为了标记为Schema的解析类。方法名字必须和schema中定义的查询名字一样。

    启动SpringBoot

    启动SpringBoot后,浏览器访问:http://localhost:8080/playground。就会进入图形界面
    在这里插入图片描述

    左边有2个按钮,DOCS和SCHEMA。DOCS会显示你定义的查询,以及可以获取到的详细信息。SCHEMA会显示你定义的SCHEMA。
    在这里插入图片描述

    在这里插入图片描述

    PostMan发起请求

    如下所示,请求地址为:localhost:8080/graphql
    请求方式为POST
    参数为{“query”:“graphql语句”}
    在这里插入图片描述

    优势

    • 前端不需要多次请求,一次请求就可以获取所需数据
    • 前端自己决定需要获取的数据
    • API文件是及时更新的

    官方页面

    语法:https://graphql.cn/learn/
    源码:https://github.com/graphql-java-kickstart/graphql-spring-boot

    展开全文
  • graphql-java-spring-boot-example 根据本教程精简了Spring Boot应用程序: 。 要运行,请执行com.example.DemoGraphQL.DemoGraphQlApplication 。 通过访问POSTMAN中的来访问graphql或访问graphiql浏览器界面的 ...
  • springboot-graphql-sqqr-jwt-demo GraphQL Java后端代表了使用Spring Boot, 和jsonwebtoken进行身份验证/授权的正确方法令牌 java qraphql实现的强大之处令人赞叹,它使使用常规jsonwebtoken和简单过滤器的实现像...
  • GraphQLSpringboot+mybatis中的结合

    千次阅读 2018-02-09 10:04:10
    1. 使用步骤1) 首先创建当前这个controller的查询器我创建了queue的查询器QueueQuery.java2) 在查询器中注入service,这个查询器就是分解了请求...可以指定某一个url使用这个graphql查询语句,我的controller是Q...

    1.      使用步骤

    1)        首先创建当前这个controller的查询器我创建了queue的查询器QueueQuery.java

    2)        在查询器中注入service,这个查询器就是分解了请求过来的方法名

    3)        在controller的某一个方法中调用,与别的url地址方法不冲突。可以指定某一个url使用这个graphql查询语句,我的controller是QueueGraphQLController

    2.      注解的使用@GraphQLQuery(name="name"),@GraphQLMutation(name= "createQueue")

    a)        在类中get方法上使用时,代表查询时候的别名,可以不爆露我们的字段。默认使用javabean的

    b)        在查询器的方法名上使用,代表查询的方法也是别名

    3.      注解@GraphQLArgument(name = "name") String name

    a)        在查询语句中使用时候,代表闯入参数的名称对应后台的名称,不可省略

    以上步骤请结合java的后台进行验证



    1.maven依赖


    <dependency>
    		    <groupId>com.graphql-java</groupId>
    		    <artifactId>graphql-java</artifactId>
    		    <version>7.0</version>
    		</dependency>
    		<dependency>
    		    <groupId>io.leangen.graphql</groupId>
    		    <artifactId>spqr</artifactId>
    		    <version>0.9.6</version>
    
    </dependency>
    
    我的controller
    package com.ewe.workorder.controller;
    
    /**
     * 用户的核心业务层
     */
    
    import com.ewe.workorder.graphquery.EchartQuery;
    import com.ewe.workorder.graphquery.WorkorderQuery;
    
    import graphql.*;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.ewe.core.exception.CustomException;
    import com.ewe.workorder.graphquery.QueueQuery;
    import com.ewe.workorder.graphquery.utils.MyGraphqlError;
    import com.ewe.workorder.graphquery.utils.MyGraphqlErrorData;
    
    import java.lang.reflect.InvocationTargetException;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    import graphql.language.SourceLocation;
    import graphql.schema.GraphQLSchema;
    import io.leangen.graphql.GraphQLSchemaGenerator;
    import io.leangen.graphql.metadata.strategy.query.AnnotatedResolverBuilder;
    import io.leangen.graphql.metadata.strategy.query.PublicResolverBuilder;
    import io.leangen.graphql.metadata.strategy.value.jackson.JacksonValueMapperFactory;
    
    @RestController
    public class GraphQLController  {
    	private final GraphQL graphQlFromAnnotated;
    	private static final Logger LOGGER = LoggerFactory.getLogger(GraphQLController.class);
    
    	@Autowired
    	/**
    	 * 这是注册器
    	 * @param orderQuery
    	 */
        public GraphQLController(QueueQuery orderQuery,WorkorderQuery workorderQuery,EchartQuery echartQuery) {
    		 //Schema generated from query classes
            GraphQLSchema schemaFromAnnotated = new GraphQLSchemaGenerator()
                    .withResolverBuilders(
                            //Resolve by annotations
                            new AnnotatedResolverBuilder(),
                            //Resolve public methods inside root package
                            new PublicResolverBuilder("com.ewe.workorder"))
                    .withOperationsFromSingleton(orderQuery)
                    .withOperationsFromSingleton(workorderQuery)
                    .withOperationsFromSingleton(echartQuery)
                    .withValueMapperFactory(new JacksonValueMapperFactory())
                    .generate();
            graphQlFromAnnotated = GraphQL.newGraphQL(schemaFromAnnotated).build();
            LOGGER.info("Generated GraphQL schema using SPQR");
            }
    
    	 	@SuppressWarnings("deprecation")
    		@PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    	    @ResponseBody
    	    public Object indexFromAnnotated(@RequestBody Map<String, Object> request) {
    	 		LOGGER.info("graphql进入");
    			String query=(String) request.get("query");
    			if(query!=null){
    				
    			}
    			else if(query==null){
    				 query=(String) request.get("mutation");
    			}
    			
    			if(query==null){
    				throw new CustomException("The option name can't be null", HttpStatus.INTERNAL_SERVER_ERROR);
    			}
    			query=query.replaceAll("\'", "\"");
    			LOGGER.info(query);
    			
    			String operationName=(String) request.get("operationName");
    			LOGGER.info(operationName);
    			long start=(new Date()).getTime();
    			ExecutionInput executionInput=new ExecutionInput(query, operationName,null,null, (Map<String,Object>)request.get("variables"));
    	        ExecutionResult executionResult = graphQlFromAnnotated.execute(executionInput);
    			/**
    			 * 使用自己定义的错误返回
    			 */
    	        if (!executionResult.getErrors().isEmpty()) {
    	        	//自定义的error
    	         	MyGraphqlErrorData data=new MyGraphqlErrorData();
    	         	MyGraphqlError error=new MyGraphqlError();
    	         	
    	        	error.setErrors(sanitize(executionResult).getErrors());
    	        	data.setData(error);
    	        	LOGGER.info(data.toString()+"");
    	        	return data;
    	        }
    
            long end=(new Date()).getTime();
    		LOGGER.info("查询耗时==>"+(end-start+"ms"));
            return executionResult;
    	}
     	 /**
     	  * 返回我们想要的message中的信息
     	  * @param executionResult
     	  * @return
     	  */
        private ExecutionResult sanitize(ExecutionResult executionResult) {
            return new ExecutionResultImpl(executionResult.getErrors().stream()
                    .peek(err -> LOGGER.error(err.getMessage()))
                    .map(this::sanitize)
                    .collect(Collectors.toList()));
        }
        /**
         * 对错误进行处理
         * @param error
         * @return
         */
        private GraphQLError sanitize(GraphQLError error) {
            if (error instanceof ExceptionWhileDataFetching) {
                return new GraphQLError() {
                    @Override
                    public String getMessage() {
                        Throwable cause = ((ExceptionWhileDataFetching) error).getException().getCause();
                        return cause instanceof InvocationTargetException ? ((InvocationTargetException) cause).getTargetException().getMessage() : cause.getMessage();
                    }
    
                    @Override
                    public List<SourceLocation> getLocations() {
                        return error.getLocations();
                    }
    
                    @Override
                    public ErrorType getErrorType() {
                        return error.getErrorType();
                    }
                };
            }
            return error;
        } 	
    }
    


    2.增加一个查询器

    package com.ewe.workorder.GraphQLQuery;
    
    import io.leangen.graphql.annotations.GraphQLArgument;
    import io.leangen.graphql.annotations.GraphQLMutation;
    import io.leangen.graphql.annotations.GraphQLQuery;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import com.ewe.workorder.controller.QueueGraphQLController;
    import com.ewe.workorder.model.Queue;
    import com.ewe.workorder.model.Users;
    import com.ewe.workorder.service.IQueueService;
    @Component
    public class QueueQuery{
    	private static final Logger LOGGER = LoggerFactory.getLogger(QueueGraphQLController.class);
    
    	@Autowired
        private IQueueService queueService;
    
        /**
         * 方法名@GraphQLQuery 去别名的意思
         * @return
         */
    	@GraphQLQuery(name = "queues")
        public List<Queue> queues() {
            return queueService.getList();
        }
    	/*
    	 * 根据queueid查询数据
    	 */
    	@GraphQLQuery(name = "queuesById")
        public Queue queuesById(@GraphQLArgument(name = "id") Integer queueId) {
    		Queue queue=queueService.selectByqueueId(queueId);
    		if(queue!=null&&queue.getUsers().size()>0){
    			LOGGER.info("size=>>{}",queue.getUsers().size());
    			//根据用户名查询信息查询信息
    			List<Users> users=new ArrayList<Users>();
    			for(Users user :queue.getUsers()){
    				users.add(queueService.selectByUserName(user.getUsername()));
    			}
    			queue.setUsers(users);
    		}
    		
    		
            return queue;
        }
    	/**
    	 * 根据用户名查询
    	 * @param userName
    	 * @return
    	 */
    	@GraphQLQuery(name = "userQueue")
        public Users queuesByuserName(@GraphQLArgument(name = "name")String userName) {
    		Users user=queueService.selectByUserName(userName);
    		if(user!=null&&user.getQueues().size()>=0)
    		{
    			LOGGER.info("queues size=>>{}",user.getQueues().size());
    			LOGGER.info("person size=>>{}",user.getQueues().get(0).getUsers().size());
    			//根据id查询信息
    			List<Queue> queues=new ArrayList<Queue>();
    			for(Queue queue :user.getQueues()){
    				queues.add(queueService.selectByqueueId(queue.getId()));
    			}
    			user.setQueues(queues);
    		}
    		
            return user;
        }
    	/**
    	 * 添加数据
    	 * @param queue
    	 * @return
    	 */
    	@GraphQLMutation(name = "createQueue")
        public Queue createQueue(@GraphQLArgument(name = "name") String name,@GraphQLArgument(name = "description")String description){
    		Queue queue=new Queue();
    		queue.setName(name);
    		queue.setDescription(description);
    		LOGGER.info(queue.getName());
            return queueService.addqueue(queue);
        } 
    /*	@GraphQLMutation(name = "createQueue")
        public Queue createQueue(@GraphQLArgument(name = "Queue")Queue queue){
        	LOGGER.info(queue.getName());
        	return queueService.addqueue(queue);
        } */
    /*	@GraphQLQuery(name = "test")
        public List<Queue> test(@GraphQLArgument(name = "Queue") Queue queue) {
    		LOGGER.info(queue.getName());
            return queueService.getList();
        }*/
    }
    

    我的Queuemodel

    package com.ewe.workorder.model;
    
    import java.util.List;
    
    import com.ewe.workorder.model.Users;
    
    import io.leangen.graphql.annotations.GraphQLQuery;
    
    public class Queue {
        private Integer id;
    
        
        private String name;
    
        private String description;
    
        List<Users> users;
        
        public List<Users> getUsers() {
    		return users;
    	}
    
    	public void setUsers(List<Users> users) {
    		this.users = users;
    	}
    	 @GraphQLQuery(name="id")
    	public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
        @GraphQLQuery(name="name")
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name == null ? null : name.trim();
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description == null ? null : description.trim();
        }
    }

    最后进行测试语句输入:

    {
    	"query":"{userQueue(name:'admin'){username,email,queues{name,users{username}}}}"
    		
    }
    

    结果
    {
        "userQueue": {
            "username": "admin",
            "email": "t3wq",
            "queues": [
                {
                    "name": "test1",
                    "users": [
                        {
                            "username": "124"
                        },
                        {
                            "username": "123"
                        }
                    ]
                },
                {
                    "name": "213",
                    "users": [
                        {
                            "username": "123"
                        }
                    ]
                },
                {
                    "name": "",
                    "users": [
                        {
                            "username": ""
                        }
                    ]
                }
            ]
        }
    }


    展开全文
  • GraphQL(二):GraphQL服务搭建中我们在pom文件中增加了如下依赖: <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-tools</artifactId> <...

    GraphQL(二):GraphQL服务搭建中我们在pom文件中增加了如下依赖:

    <dependency>
        <groupId>com.graphql-java</groupId>
        <artifactId>graphql-java-tools</artifactId>
        <version>4.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.graphql-java</groupId>
        <artifactId>graphiql-spring-boot-starter</artifactId>
        <version>3.6.0</version>
    </dependency>
    <dependency>
        <groupId>com.graphql-java</groupId>
        <artifactId>graphql-spring-boot-starter</artifactId>
        <version>3.6.0</version>
    </dependency>
    复制代码

    接下来我们来分析其中的部分原理。

    添加了上述依赖后,会引入这么几个jar包:

    1. graphiql-spring-boot-autoconfigure: 开发者工具graphiql的自动配置jar包
    2. graphiql-spring-boot-starter: 开发者工具graphiql的实现
    3. graphql-java: graphql的java实现
    4. graphql-java-servlet: 封装graphql服务为servlet,处理graphql的request和response
    5. graphql-java-tools: 自动加载*.graphqls文件,并屏蔽graphql-java的底层实现细节
    6. graphql-spring-boot-autoconfigure: graphql-spring-boot的自动配置jar包
    7. graphql-spring-boot-starter: starter

    开发者工具的两个包暂不讨论。一切都是从graphql-spring-boot-autoconfigure开始的,通过graphql-spring-boot-autoconfigure完成了GraphQLServlet的自动配置。

    @Configuration
    @ConfigurationProperties(prefix = "graphql.servlet")
    public class GraphQLServletProperties {
    
        private String mapping;
    
        public String getMapping() {
            return mapping != null ? mapping : "/graphql";
        }
    
        //省略
    }
    
    复制代码

    在GraphQLServletProperties配置类上启动了ConfigurationProperties,前缀是"graphql.servlet",因此我们可以在application.properties中以"graphql.servlet"开头进行配置,比如将endpoint从默认的“/graphql”改为“/school”:

    graphql.servlet.mapping=/school
    复制代码

    同样的,在GraphQLWebAutoConfiguration配置类中可以找到关于是否启用GraphQLServlet和跨域访问的配置。

    GraphQLServlet

    通过graphql-spring-boot-autoconfigure,SpringBoot会自动扫描到GraphQLServlet的相关配置信息,在GraphQLServlet的构造函数中初始化了getHandler和postHandler分别用于处理get和post请求

    和Spring的DispatcherServlet不一样,GraphQLServlet重写了doGet和doPost方法,同时GraphQLServlet并不包含拦截器(DispatcherServlet请求执行过程),GraphQL提供了一个GraphQLServletListener接口,允许我们针对请求执行结果做处理:

    private void doRequest(HttpServletRequest request, HttpServletResponse response, RequestHandler handler) {
    
        List<GraphQLServletListener.RequestCallback> requestCallbacks = runListeners(l -> l.onRequest(request, response));
    
        try {
            handler.handle(request, response);
            runCallbacks(requestCallbacks, c -> c.onSuccess(request, response));
        } catch (Throwable t) {
            response.setStatus(500);
            log.error("Error executing GraphQL request!", t);
            runCallbacks(requestCallbacks, c -> c.onError(request, response, t));
        } finally {
            runCallbacks(requestCallbacks, c -> c.onFinally(request, response));
        }
    }
    复制代码

    那么,如果要在GraphQL中实现拦截器的功能要怎么做呢?

    GraphQL提供了一个Instrumentation接口:

    允许我们在执行前、解析前、验证前、数据获取前、字段数据获取前(最后两个是一样的作用)插入自己的逻辑,但是它跟Spring的拦截器不一样,它没有提供跳过执行的功能,要拦截掉执行只能抛出异常。

    FiledResolverScanner

    GraphQL(二):GraphQL服务搭建中我们提到,实现Resolver需要满足如下约定:

    1. <field>
    2. is<field> – only if the field is of type Boolean
    3. get<field>
    4. getField<field>(最新版增加的契约)
    复制代码

    关于这部分契约的定义在官方文档中并没有找到,那就从源代码去找是如何定义契约。

    在graphql-java-tools(4.0.0版本)中,可以找到一个FieldResolverScanner类,负责了FieldResolver的扫描,找到方法findResolverMethod:

    private fun findResolverMethod(field: FieldDefinition, search: Search): java.lang.reflect.Method? {
    
        val methods = getAllMethods(search.type)
        val argumentCount = field.inputValueDefinitions.size + if(search.requiredFirstParameterType != null) 1 else 0
        val name = field.name
    
        val isBoolean = isBoolean(field.type)
    
        // Check for the following one by one:
        //   1. Method with exact field name
        //   2. Method that returns a boolean with "is" style getter
        //   3. Method with "get" style getter
        return methods.find {
            it.name == name && verifyMethodArguments(it, argumentCount, search)
        } ?: methods.find {
            (isBoolean && it.name == "is${name.capitalize()}") && verifyMethodArguments(it, argumentCount, search)
        } ?: methods.find {
            it.name == "get${name.capitalize()}" && verifyMethodArguments(it, argumentCount, search)
        }
    }
    复制代码

    这就是定义以上契约的地方。

    转载于:https://juejin.im/post/5ce55c135188252dd8060d76

    展开全文
  • Springboot中集成GraphQL

    千次阅读 2020-07-09 10:35:26
    Springboot中集成GraphQL 1.前言 最近项目需求要使用GraphQL做为后端服务,提供API接口,奈何资料较少,在此记录一下搭建GraphQL服务的过程 2.工程环境 工程包含主要插件 JDK 1.8 Springboot 2.1.3 MySQL MyBatis ...

    Springboot中集成GraphQL

    1.前言

    最近项目需求要使用GraphQL做为后端服务,提供API接口,奈何资料较少,在此记录一下搭建GraphQL服务的过程

    2.工程环境

    工程包含主要插件
    JDK 1.8
    Springboot 2.1.3
    MySQL
    MyBatis
    Graphql-java
    fastjson
    Lombok
    maven

    3.创建Springboot工程

    这里不在描述,使用IDEA或者Eclipse创建一个springboot工程或者导入一个Springboot工程都行,这里使用2.1.3.RELEASE版本

    4.集成开发环境

    4.1 配置pom.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.mx.server</groupId>
        <artifactId>spring-boot-graphql</artifactId>
        <version>1.0.0-RELEASE</version>
        <name>spring-boot-graphql</name>
        <description>easy graphql for spring boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <kotlin.version>1.3.70</kotlin.version>
            <mybatis.version>2.0.0</mybatis.version>
            <c3p0.version>0.9.5.2</c3p0.version>
            <fastjson.version>1.2.5</fastjson.version>
            <junit.jupiter.version>5.6.2</junit.jupiter.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <!-- 使用Junit5,排除spring-test自带的junit4 -->
                <exclusions>
                    <exclusion>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- GraphQL -->
            <dependency>
                <groupId>com.graphql-java-kickstart</groupId>
                <artifactId>graphql-spring-boot-starter</artifactId>
                <version>7.0.1</version>
            </dependency>
            <dependency>
                <groupId>com.graphql-java-kickstart</groupId>
                <artifactId>graphql-java-tools</artifactId>
                <version>6.0.2</version>
            </dependency>
            <!-- GraphiQL tool -->
            <dependency>
                <groupId>com.graphql-java-kickstart</groupId>
                <artifactId>graphiql-spring-boot-starter</artifactId>
                <version>7.0.1</version>
                <scope>runtime</scope>
            </dependency>
            <!-- Log4J2 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-log4j2</artifactId>
            </dependency>
            <!-- JDBC -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <!-- MySQL -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!-- Mybatis -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <!-- c3p0 -->
            <dependency>
                <groupId>com.mchange</groupId>
                <artifactId>c3p0</artifactId>
                <version>${c3p0.version}</version>
            </dependency>
            <!-- FastJSON -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <!-- lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!-- Junit5 -->
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter</artifactId>
                <version>${junit.jupiter.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    其中GraphQL关键依赖参考GraphQL Java Kickstart官网

    4.2 配置application.yml文件

    Springboot的配置文件为applicaion.properties或者applicaion.yml格式,根据自己使用习惯选择即可,这里使用application.yml风格

    #Server
    server:
      port: 8080
    
    #Mysql
    spring:
      datasource:
        c3p0:
          driverClass: com.mysql.cj.jdbc.Driver
          jdbcUrl: jdbc:mysql://localhost:3306/graphql_test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false
          user: mysql
          password: mxnavi
    
    #Mybatis
    mybatis:
      config-location: classpath:mybatis/mybatis-config.xml
      mapper-locations: classpath*:mybatis/mapper/**/*.xml
    
    #GraphQL
    graphql:
      servlet:
        mapping: /graphql
        enabled: true
        corsEnabled: true
        cors:
          allowed-origins: http://some.domain.com
        # if you want to @ExceptionHandler annotation for custom GraphQLErrors
        exception-handlers-enabled: true
        contextSetting: PER_REQUEST_WITH_INSTRUMENTATION
      tools:
        schema-location-pattern: "**/*.graphqls"
        # Enable or disable the introspection query. Disabling it puts your server in contravention of the GraphQL
        # specification and expectations of most clients, so use this option with caution
    
    #GraphiQL Tool
    graphiql:
      mapping: /graphiql
      endpoint:
        graphql: /graphql
      subscriptions:
        timeout: 30
        reconnect: false
      static:
        basePath: /
      enabled: true
      pageTitle: GraphiQL
      props:
        resources:
          query: /graphql/types.graphqls
          variables: /graphql/types.graphqls
        variables:
          editorTheme: "solarized light"
      headers:
        Authorization: "Bearer <your-token>"
    

    主要配置包括
    Server部分:配置一些服务的基础,可定义端口号,服务名等
    MySQL部分:配置连接MySQL数据库相关参数
    MyBatis部分:配置MyBatis相关参数
    GraphQL部分:配置GraphQL相关参数
    GraphiQL Tool部分:配置GraphiQL工具相关参数,可访问图形化界面

    4.3 创建graphqls文件

    在resource目录下创建graphql文件夹,日后所有的graphqls文件都存放再此,创建types.graphqls文件
    注意:此文件types.graphqls与applicaion.yml中配置有对应关系,如果修改,请记得一起修改,否则启动报错
    types.graphqls文件内容如下

    type Query {
        user(nickname: String!): User
        users: [User]
    }
    type Mutation {
        addUser(mail: String!, nickname: String!, password: String!, description: String): Result
        deleteUser(id: String!): Result
        updateUser(id: String!, mail: String!, nickname: String!, description: String): User
        addUserByInput(input: AddUserInput): User
    }
    type User {
        id: String!
        mail: String!
        nickname: String!
        password: String!
        description: String
    }
    
    type Result {
        respCode: Int!
        msg: String
    }
    
    input AddUserInput {
        mail: String!
        nickname: String!
        password: String!
        description: String
    }
    

    其中Query下定义的是查询相关接口,Mutation下定义的是修改相关接口
    Query中user和users可以理解为controller中的接口名,后续需要和Resolver中方法名对应
    关于graphqls文件中写法,具体参考GraphQL官网介绍GraphQL中文网

    4.4 创建graphqls文件对应的Java对象

    User.java

    package com.mx.server.entity;
    
    import lombok.Data;
    
    @Data
    public class User {
        private String id;
        private String nickname;
        private String mail;
        private String password;
        private String description;
        private String updateTime;
        private String createTime;
    }
    

    Result.java

    package com.mx.server.entity.response;
    
    import lombok.Data;
    
    @Data
    public class Result {
        private Integer respCode;
        private String msg;
    }
    

    AddUserInput.java

    package com.mx.server.entity.input;
    
    import lombok.Data;
    
    @Data
    public class AddUserInput {
        private String nickname;
        private String mail;
        private String password;
        private String description;
    }
    

    4.5 创建graphqls文件对应的Resolver对象

    想实现graphqls文件中Query中的方法,就实现GraphQLQueryResolver
    想实现graphqls文件中Mutation的中的方法,就实现GraphQLMutationResolver
    这里为了区分Query和Mutation,分成2个Resolver来写,具体在项目中应用时根据实际情况来修改。
    QueryResolver.Java

    package com.mx.server.resolvers;
    
    import com.mx.server.entity.User;
    import com.mx.server.service.UserService;
    import graphql.kickstart.tools.GraphQLQueryResolver;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Component
    public class QueryResolver implements GraphQLQueryResolver {
    
        private static final Logger logger = LogManager.getLogger(QueryResolver.class);
    
        @Resource
        private UserService userService;
    
        public User user(String nickname) {
            logger.info("Query Resolver ==> user");
            logger.info("params: nickname:{}", nickname);
            return userService.getUserByNickname(nickname);
        }
    
        public List<User> users() {
            logger.info("Query Resolver ==> users");
            return userService.listUsers();
        }
    }
    

    MutationResolver.java

    package com.mx.server.resolvers;
    
    import com.mx.server.entity.User;
    import com.mx.server.entity.input.AddUserInput;
    import com.mx.server.entity.response.ResponseBuilder;
    import com.mx.server.entity.response.Result;
    import com.mx.server.service.UserService;
    import graphql.kickstart.tools.GraphQLMutationResolver;
    import graphql.kickstart.tools.GraphQLQueryResolver;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    
    @Component
    public class MutationResolver implements GraphQLQueryResolver, GraphQLMutationResolver {
    
        private static final Logger logger = LogManager.getLogger(MutationResolver.class);
    
        @Resource
        private UserService userService;
    
        public Result addUser(String mail, String nickname, String password, String description) {
            logger.info("Mutation Resolver ==> addUser");
            logger.info("params: mail:{}, nickname:{}, password:{}, description:{}",
                    mail, nickname, password, description);
            return userService.addUser(mail, nickname, password, description);
        }
    
        public Result deleteUser(String id) {
            if(StringUtils.isAnyBlank(id)){
                return ResponseBuilder.getRespCodeMsg(-101, "参数缺失");
            }
            logger.info("Mutation Resolver ==> deleteUser");
            logger.info("params: id:{}", id);
            return userService.deleteUser(id);
        }
        
        public User updateUser(String id, String mail, String nickname, String description) {
            logger.info("Mutation Resolver ==> updateUser");
            logger.info("params: id:{}, mail:{}, nickname:{}, description:{}",
                    id, mail, nickname, description);
            return userService.updateUser(id, mail, nickname, description);
        }
    
        public User addUserByInput(AddUserInput addUserInput){
            logger.info("Mutation Resolver ==> addUserByInput");
            logger.info("params: {}", addUserInput);
            return userService.addUserInput(addUserInput);
        }
    }
    

    注意:Resolver中的方法,入参及返回值类型,必须和graphqls文件中定义的一致,否则启动报错。
    至此与GraphQL相关的配置及代码都已创建完成,接下来把工程中其他的类都一一实现,比如Server,Dao等。

    4.6 创建Service,Dao等实现类

    UserService.java -> Service类,通过Resolver调用,简单加了一些验证。

    package com.mx.server.service;
    
    import com.mx.server.dao.UserMapper;
    import com.mx.server.entity.User;
    import com.mx.server.entity.input.AddUserInput;
    import com.mx.server.entity.response.ResponseBuilder;
    import com.mx.server.entity.response.Result;
    import com.mx.server.util.CommonUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class UserService {
    
        @Resource
        private UserMapper userMapper;
    
        private static final Logger logger = LogManager.getLogger(UserService.class.getName());
    
        public User getUserByNickname(String nickname){
            logger.info("Service ==> getUserByNickname");
            return userMapper.getUserByNickname(nickname);
        }
    
        public List<User> listUsers(){
            logger.info("Service ==> listUsers");
            return userMapper.listUsers();
        }
    
        public Result addUser(String mail, String nickname, String password, String description){
            logger.info("Service ==> getUser");
    
            User userDb = userMapper.getUserByNickname(nickname);
            if (null != userDb){
                return ResponseBuilder.getRespCodeMsg(-110, "用户昵称存在");
            }
    
            User addUser = new User();
            addUser.setId(CommonUtils.getUUID());
            addUser.setMail(mail);
            addUser.setNickname(nickname);
            addUser.setPassword(password);
            addUser.setDescription(description);
    
            userMapper.addUser(addUser);
    
            return ResponseBuilder.getRespCodeMsg(100, "Success");
        }
    
        public Result deleteUser(String id){
            logger.info("Service ==> deleteUser");
    
            User user = userMapper.getUserById(id);
            if (null == user){
                return ResponseBuilder.getRespCodeMsg(-500, "数据不存在");
            }
    
            userMapper.deleteUser(id);
            return ResponseBuilder.getRespCodeMsg(100, "Success");
        }
    
        public User updateUser(String id,String mail, String nickname, String description){
            logger.info("Service ==> updateUser");
            User updateUser = new User();
            updateUser.setId(id);
            updateUser.setMail(mail);
            updateUser.setNickname(nickname);
            updateUser.setDescription(description);
    
            userMapper.updateUser(updateUser);
    
            return updateUser;
        }
    
        public User addUserInput(AddUserInput addUserInput){
            logger.info("Service ==> addUserInput");
            User addUser = new User();
            addUser.setId(CommonUtils.getUUID());
            addUser.setMail(addUserInput.getMail());
            addUser.setNickname(addUserInput.getNickname());
            addUser.setPassword(addUserInput.getPassword());
            addUser.setDescription(addUserInput.getDescription());
    
            userMapper.addUser(addUser);
    
            return addUser;
        }
    }
    

    UserMapper.java -> Mapper接口类,定义调用数据库的方法,由Service调用。

    package com.mx.server.dao;
    
    import com.mx.server.entity.User;
    import org.apache.ibatis.annotations.Mapper;
    
    import java.util.List;
    
    @Mapper
    public interface UserMapper {
        User getUserById(String id);
        User getUserByNickname(String nickname);
        List<User> listUsers();
        void addUser(User user);
        void deleteUser(String id);
        void updateUser(User user);
    }
    

    CommonUtils.java -> 工具类,定义一些常用公共方法

    package com.mx.server.util;
    
    import java.util.UUID;
    
    public final class CommonUtils {
    
        private CommonUtils() {
        }
    
        public static String getUUID() {
            return UUID.randomUUID().toString().replaceAll("-", "");
        }
    }
    

    DataSourceConfig -> 定义数据源

    package com.mx.server.config;
    
    import javax.sql.DataSource;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    @Configuration
    // 声明为配置类,相当于<beans>标签
    @Component
    public class DataSourceConfig {
    
        @Bean(name = "dataSource")
        // 对象及名称,相当于<bean>标签
        @Primary
        // 主要的候选者
        // 配置属性,prefix : 前缀 spring.datasource固定
        @ConfigurationProperties(prefix = "spring.datasource.c3p0")
        public DataSource createDataSource() {
            return DataSourceBuilder.create() // 创建数据源构建对象
                    .type(ComboPooledDataSource.class) // 设置数据源类型
                    .build(); // 构建数据源对象
        }
    
    }
    

    4.7 MyBatis配置

    本工程使用了MyBatis,上面已经定义好了Mapper类,下面就来创建mapper对应的xml文件实现对MySql的CRUD操作。
    applicaion.yml文件中的MyBatis配置如下:

    #Mybatis
    mybatis:
      config-location: classpath:mybatis/mybatis-config.xml
      mapper-locations: classpath*:mybatis/mapper/**/*.xml
    

    在resource目录下创建mybatis文件夹,放入mybatis-config.xml,其实没什么有用的内容,简单定义一些返回类型的别名。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <typeAliases>
            <typeAlias alias="Integer" type="java.lang.Integer" />
            <typeAlias alias="Long" type="java.lang.Long" />
            <typeAlias alias="Boolean" type="java.lang.Boolean" />
            <typeAlias alias="HashMap" type="java.util.HashMap" />
            <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
            <typeAlias alias="ArrayList" type="java.util.ArrayList" />
            <typeAlias alias="LinkedList" type="java.util.LinkedList" />
        </typeAliases>
    </configuration>
    

    在mybatis文件夹下创建mapper文件夹,用来存放具体实现mapper的xml文件
    userMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.mx.server.dao.UserMapper">
        <select id="getUserByNickname" resultType="com.mx.server.entity.User">
            SELECT
                id,
                nickname,
                mail,
                description,
                DATE_FORMAT(update_time,'%Y-%m-%d %H:%i') updateTime,
                DATE_FORMAT(create_time,'%Y-%m-%d %H:%i') createTime
            FROM
                user
            WHERE
                nickname = #{nickname}
        </select>
    
        <select id="getUserById" resultType="com.mx.server.entity.User">
            SELECT
                id,
                nickname,
                mail,
                description,
                DATE_FORMAT(update_time,'%Y-%m-%d %H:%i') updateTime,
                DATE_FORMAT(create_time,'%Y-%m-%d %H:%i') createTime
            FROM
                user
            WHERE
                id = #{id}
        </select>
    
        <select id="listUsers" resultType="com.mx.server.entity.User">
            SELECT
                id id,
                nickname nickname,
                mail mail,
                description description,
                DATE_FORMAT(update_time,'%Y-%m-%d %H:%i:%s') updateTime,
                DATE_FORMAT(create_time,'%Y-%m-%d %H:%i:%s') createTime
            FROM
                user
        </select>
    
        <insert id="addUser" parameterType="com.mx.server.entity.User"
                keyProperty="id" useGeneratedKeys="true">
            INSERT INTO
                user
            (
                id,
                nickname,
                password,
                mail,
                description,
                update_time,
                create_time
            )
            VALUES
            (
                #{id},
                #{nickname},
                #{password},
                #{mail},
                #{description},
                now(),
                now()
            )
        </insert>
    
        <update id="updateUser">
            UPDATE
                user
            SET
                nickname = #{nickname},
                mail = #{mail},
                description = #{description},
                update_time = now()
            WHERE
                id = #{id}
        </update>
    
        <delete id="deleteUser">
            DELETE FROM
                user
            WHERE
                id = #{id}
        </delete>
    </mapper>
    

    user表结构

    CREATE TABLE `user` (
      `id` varchar(32) NOT NULL,
      `nickname` varchar(255) NOT NULL,
      `mail` varchar(255) NOT NULL,
      `password` varchar(255) NOT NULL,
      `description` varchar(255) DEFAULT NULL,
      `update_time` datetime DEFAULT NULL,
      `create_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    

    最终工程目录结构如下
    在这里插入图片描述

    5.启动和测试执行

    使用GraphiQL Tool进行API调用测试,启动Application,成功后,打开浏览器访问 http://localhost:8080/graphiql
    http://localhost:8080/graphiql
    默认显示工程里的graphqls文件内容,删除后,可在右上角Docs菜单查看

    5.1 Query类型调用

    调用查询接口users,输入调用内容,点击执行按钮,查看结果

    # query 查询关键字
    # ListUsers 别名
    # users 接口名
    # id,nickname 查询后,想要获取的返回值
    query ListUsers{
        users {
          id
          nickname
        }
    }
    
    

    执行结果
    users

    5.2 Mutation类型调用

    调用创建接口addUser,输入调用内容,点击执行按钮,查看结果

    # mutation 变更关键字
    # AddUser 别名
    # addUser 接口名,后面接入参,接口中有!定义的变量是必填的,其他随意
    # respCode,msg 想要获取的返回值
    mutation AddUser{
        addUser(mail:"11", nickname: "testuser", password: "111", description: "test") {
          respCode
          msg
        }
    }
    

    执行结果
    addUser

    5.3 使用变量Variables

    拿根据昵称查询用户做示例

    # QueryUser 别名
    # query 关键字后面的 $nickname 为声明变量
    # user 后面的 $nickname 为使用变量,变量名要与上面声明的一致
    query QueryUser($nickname: String!){
        user(nickname: $nickname) {
          id
          nickname
        }
    }
    

    在Variables中写入变量具体的值,格式为JSON格式,这里输入刚刚创建的用户,昵称为testuser

    {
      "nickname": "testuser"
    }
    

    执行结果
    在这里插入图片描述

    5.4 input输入类型的使用

    在实际应用时,如果入参过多,逐个填写,显示过于太长了,使用起来也不方便,下面来演示使用input类型,将入参封装成对象传入,接口列表中定义了addUserByInput方法,入参类型为input

    # mutation 变更关键字
    # AddUserByInput 别名
    # mutation后$userinput,声明userinput变量,类型为AddUserInput
    # 变量的类型AddUserInput,已经在graphqls文件中定义,input开头那个
    # addUserByInput 接口名
    # addUserByInput后input 入参类型 
    # addUserByInput后$userinput 入参变量引用
    mutation AddUserByInput($userinput: AddUserInput) {
      addUserByInput(input: $userinput) {
        id
        nickname
        mail
      }
    }
    

    在Variables中写入变量userinput具体的值,格式为JSON格式

    {
      "userinput": {
        "mail": "testuser2@test.com",
        "nickname": "testuser2",
        "password": "123456"
      }
    }
    

    执行结果
    addUserByInput
    到此基本功能演示结束,好了,接下就根据官网探索更多的使用方式吧!

    6.源码

    GitHub源码地址

    展开全文
  • 因为一个微服务通常只会定义一个.graphqls文件 所以这个文件会包含要通过graphql查询的所有不同实体类 schema { query: UserQuery } type UserQuery { #查询实体类一:User,对应查询名称也为User User(id:...
  • springboot 整合GraphQL-java

    千次阅读 2019-11-08 14:15:11
    这里就不再介绍GraphQL了,有兴趣的同学可以看https://blog.csdn.net/qq_40794266/article/details/102961334 导入依赖 <!--spring boot的支持--> <parent> <groupId>org.springframework....
  • 有必要记住的是,除了作为 schema 的入口,Query和Mutation类型与其它 GraphQL 对象类型别无二致,它们的字段也是一样的工作方式。 在简单学习-1里面,我们搭建了一个springboot项目,并且引入了graphiql的jar,...
  • springboot集成graphql(一)

    千次阅读 2019-11-03 23:24:23
    背景:基于上篇springboot集成jooq继续 GraphQL Playgroundhttps://github.com/prisma-labs/graphql-playground/releases 一、pom.xml文件引入相关依赖 <dependency> <groupId>...
  •  如果你的springboot项目为1.5.x,那么你可以引用下面的版本GraphQL。 <!--graphQL--> <groupId>com.graphql-java</groupId> <artifactId>graphql-spring-boot-starter <version>3.10.0 ...
  • GraphQL我的理解是,原来传统restful返回的json,只能是服务端返回来什么,...这里记录一下GraphQLspringboot+mybatis整合的过程,方便以后查看,如有错误或者不足的地方,欢迎指出。 首先添加GraphQL依赖 ...
  • 1. root.graphql中定义的 查询在query中一定要有 不然报错 2. 其他按 正常的springboot+mybatis 的方式编写即可。 软件赚钱_优米谷
  • springBoot+graphql+graphiql 基础工程搭建

    千次阅读 2018-12-26 16:30:40
    springBoot+graphql+graphiql 基础工程搭建 项目构建 工程目录 添加依赖: &amp;amp;lt;dependency&amp;amp;gt; &amp;amp;lt;groupId&amp;amp;gt;org.projectlombok&amp;amp;lt;/...
  • GraphQL是API的查询语言。通常,在我们的API中创建REST端点时,通常的趋势是为需求创建端点。假设您的端点返回了一个员工列表,每个员工都有一般属性,例如姓名,年龄和地址(假设地址是映射到员工的另一个模型,每...
  • GraphQL实战-第三篇-spring Boot 实现 在上一篇的java实现中,分享了使用GraphQL的基本流程,接下来分享一下在Spring Boot中对GraphQL的应用 首先创建一个Spring Boot的项目 POM依赖 <?xml version="1.0" ...
  • GraphQL Spring Boot 使用

    千次阅读 2019-01-29 19:55:30
    GraphQL Spring Boot 使用 项目地址 ...使用 SpringBootGraphQL 创建一个最简单的增删改查接口应用,使用 MongoDB 存储数据 创建应用 添加依赖 dependencies { implementation('org.spring...
  • 相关历史文章(阅读本文之前,您可能需要先看下之前的系列????) 国内最全的SpringBoot...GraphQL的探索之路 – SpringBoot集成GraphQL小栗子篇二 - 第315篇 师傅:徒儿,研究如何了,看你最近都悠哉悠哉的,...
  • GraphQL是Facebook在2012年开发的一种数据查询语言,用于解决REST API和传统数据库模型的缺点。 通常,当程序员编写REST API数据查询时,他们默认只在需要一部分数据时才检索整个数据结构。 例如,...
  • 本文中搭建的项目是在参考company的真实项目基础上简化而来,主要简化了许多业务逻辑、一些数据源(比如OTS redis在项目中的使用以及web权限验证等等,,),项目变得简单清晰,突出使用SpringBoot集成GraphQL服务,...
  • 引入springboot相应jar包,这里指指明使用的graphql相关jar包 <!-- graphql --> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-spring-boot-starter<...
  • DGS FrameworkNetflix 开发的 GraphQL 服务器框架 Domain Graph Service(DGS)框架的源代码 ,该框架是为独立和联合 GraphQL 服务开发的内容流服务,用于简化 GraphQL 的实现 Netflix 在 2019 年开始实施联合 ...
  • GraphQL 是一种为 API 接口和查询已有数据运行时环境的查询语言. 它提供了一套完整的和易于理解的 API 接口数据描述, 给客户端权力去精准查询他们需要的数据, 而不用再去实现其他更多的代码, 使 API 接口开发变得更...
  • 什么是GraphQL GraphQL 是一种用于 API 的查询语言。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,...
  • 今天为了整合mybatis到我的graphql项目里面,真的是搞得我焦头烂额啊,踩坑让我成长,怎么说,这也是宝贵的学习经验,希望以后处理问题的时候可以更高效一点,先放出整合后的源代码镇帖: ...
  • 安装 JS GraphQL 插件
  • springboot集成graphql异常集合

    千次阅读 2019-11-21 23:11:23
    环境背景:JDK11+、springboot-2.2.0.RELEASE、graphiql-spring-boot-starter-5.10.0、graphql-java-2019-10-31T04-37-48-0919e71 一、java.lang.IllegalStateException: javax.websocket.server.ServerContainer ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 652
精华内容 260
关键字:

graphqlspringboot

spring 订阅