吳謹(jǐn)含廠家不愿做網(wǎng)站/平臺(tái)網(wǎng)站開發(fā)公司
文章目錄
- 1、數(shù)據(jù)類型
- 1.1、標(biāo)量類型
- 1.2. 高級(jí)數(shù)據(jù)類型
- 基本操作
- 2、Spring for GraphQL實(shí)例
- 2.1、項(xiàng)目目錄
- 2.2、數(shù)據(jù)庫表
- 2.3、GraphQL的schema.graphql
- 2.4、Java代碼
- 3、運(yùn)行效果
- 3.1、添加用戶
- 3.2、添加日志
- 3.3、查詢所有日志
- 3.4、查詢指定用戶日志
- 3.5、數(shù)據(jù)訂閱
- 4、總結(jié)
GraphQL是一種用于API開發(fā)的查詢語言和運(yùn)行時(shí)環(huán)境。它由Facebook開發(fā)并于2015年開源。GraphQL的主要目標(biāo)是提供一種更高效、靈活和易于使用的方式來獲取和操作數(shù)據(jù)。與傳統(tǒng)的RESTful API相比,GraphQL允許客戶端精確地指定需要的數(shù)據(jù),并減少了不必要的網(wǎng)絡(luò)傳輸和數(shù)據(jù)處理。
采用GraphQL,甚至不需要有任何的接口文檔,在定義了Schema之后,服務(wù)端實(shí)現(xiàn)Schema,客戶端可以查看Schema,然后構(gòu)建出自己需要的查詢請求來獲得自己需要的數(shù)據(jù)。
1、數(shù)據(jù)類型
1.1、標(biāo)量類型
- Int -32位整型數(shù)字;
- Float-雙精度浮點(diǎn)型;
- String-UTF‐8 字符序列;
- Boolean-布爾型,true 或者 false;
- ID-標(biāo)識(shí)類型,唯一標(biāo)識(shí)符,注意此ID為字符,如果使用Mysql自增長id,也會(huì)自動(dòng)轉(zhuǎn)為對(duì)應(yīng)的字符串;
1.2. 高級(jí)數(shù)據(jù)類型
- Object - 對(duì)象,用于描述層級(jí)或者樹形數(shù)據(jù)結(jié)構(gòu)。Object類型有一個(gè)類型名,以及類型包含的字段。
type Product {id: ID!info: String!price: Float
}
在此示例中,聲明了Product對(duì)象類型,定義了3 個(gè)字段:
id:非空 ID 類型。
info:非空字符串類型。
price:浮點(diǎn)型。
- Interface-接口,用于描述多個(gè)類型的通用字;與 Object一樣。
interface Product {id: ID!info: String!price: Float
}
- Union-聯(lián)合類型,用于描述某個(gè)字段能夠支持的所有返回類型以及具體請求真正的返回類型;
- Enum-枚舉,用于表示可枚舉數(shù)據(jù)結(jié)構(gòu)的類型;
enum Status {YesNo
}
type Product {id: ID!info: String!price: Floatstat: Status
}
- Input-輸入類型input本質(zhì)上也是一個(gè)type類型,是作為Mutation接口的輸入?yún)?shù)。換言之,想要定義一個(gè)修改接口(add,update,delete)的輸入?yún)?shù)對(duì)象,就必須定義一個(gè)input輸入類型。
input BookInput {isbn: ID!title: String!pages: IntauthorIdCardNo: String
}
- List -列表,任何用方括號(hào) ([]) 括起來的類型都會(huì)成為 List 類型。
type Product {id: ID!info: Stringprice: Floatimages: [String]
}
- Non-Null-不能為 Null,類型后邊加!表示非空類型。例如,String 是一個(gè)可為空的字符串,而String!是必需的字符串。
基本操作
- Query(只讀操作)
#schema.graphqls定義操作
type Query {allBooks: [Book]!bookByIsbn(isbn: ID): Book
}# 接口查詢語法
query{allBooks {titleauthor {nameage}}
}
- Mutation(可寫操作)
#schema.graphqls定義操作
type Mutation {createBook(bookInput: BookInput): BookcreateAuthor(authorInput: AuthorInput): Author
}# mutation{
# createAuthor(authorInput:{
# idCardNo: "341234567891234567",
# name:"test1",
# age:38
# }
# ){
# name
# age
# }
# }
- Subscription(訂閱操作)
type Subscription {greetings: String
}
2、Spring for GraphQL實(shí)例
GraphQL只是一種架構(gòu)設(shè)計(jì),具體的實(shí)現(xiàn)需要各個(gè)技術(shù)平臺(tái)自己實(shí)現(xiàn),目前主流的開發(fā)語言基本都已經(jīng)有現(xiàn)成的類庫可以使用,GraphQL Java就是Java平臺(tái)的實(shí)現(xiàn)。
GraphQL Java雖然實(shí)現(xiàn)了GraphQL,但是只是一個(gè)執(zhí)行GraphQL請求的引擎,用戶在使用中需要?jiǎng)?chuàng)建自己的HTTP服務(wù)來提供服務(wù)。
Spring for GraphQL為基于GraphQL Java構(gòu)建的Spring應(yīng)用程序提供支持,來自 GraphQL Java 團(tuán)隊(duì),它的目標(biāo)是成為所有Spring、GraphQL應(yīng)用程序的基礎(chǔ)。
spring-graphql中定義的核心注解如下:
- @GraphQlController:申明該類是GraphQL應(yīng)用中的控制器
- @QueryMapping:申明該類或方法使用GraphQL的query操作,等同于@SchemaMapping(typeName = “Query”),類似于@GetMapping
- @Argument:申明該參數(shù)是GraphQL應(yīng)用的入?yún)?/li>
- @MutationMapping:申明該類或方法使用GraphQL的mutation操作,等同于@SchemaMapping(typeName = “Mutation”)
- @SubscriptionMapping:申明該類或方法使用GraphQL的subscription操作,等同于@SchemaMapping(typeName = “Subscription”)
- @SchemaMapping:指定GraphQL操作類型的注解,類似@RequestMapping
2.1、項(xiàng)目目錄
項(xiàng)目代碼目錄
2.2、數(shù)據(jù)庫表
數(shù)據(jù)表結(jié)構(gòu),這里例子簡單用了用戶表和用戶日志表
CREATE TABLE `admin_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4CREATE TABLE `admin_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`user_id` varchar(255) DEFAULT NULL,`visit_url` varchar(255) DEFAULT NULL,`create_date` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4
2.3、GraphQL的schema.graphql
GraphQL對(duì)應(yīng)的schema.graphql定義文件,注意GraphQL默認(rèn)只支持標(biāo)量類型,DateTime自定義類型使用graphql-java-extended-scalars:https://github.com/graphql-java/graphql-java-extended-scalars提供
scalar DateTimetype AdminUser{id: ID!name: String!
}type AdminLog{id: ID!visitUrl: Stringuser: AdminUser!createDate: DateTime
}type Query {allLogs:[AdminLog]logByUser(userid: ID): [AdminLog]
}type Mutation {createUser(adminUserInput: AdminUserInput): AdminUsercreateLog(adminLogInput: AdminLogInput): AdminLog
}input AdminLogInput {userId: String!visitUrl: StringcreateDate: DateTime
}input AdminUserInput {name: String!
}type Subscription {greetings: String
}
2.4、Java代碼
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>3.0.10</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.penngo.example</groupId><artifactId>graphql-app</artifactId><version>0.0.1-SNAPSHOT</version><name>graphql-app</name><description>graphql-app project for Spring Boot</description><properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-graphql</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version></dependency><!-- https://mvnrepository.com/artifact/com.graphql-java/graphql-java-extended-scalars --><dependency><groupId>com.graphql-java</groupId><artifactId>graphql-java-extended-scalars</artifactId><version>19.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build><repositories><repository><id>alimaven</id><name>Maven Aliyun Mirror</name><url>https://maven.aliyun.com/repository/central</url></repository></repositories><pluginRepositories><pluginRepository><id>aliyun-plugin</id><url>https://maven.aliyun.com/repository/public</url><releases><enabled>true</enabled></releases><snapshots><enabled>false</enabled></snapshots></pluginRepository></pluginRepositories></project>
對(duì)應(yīng)的數(shù)據(jù)庫實(shí)體類
package com.penngo.example.entity;import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.OffsetDateTime;@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminLog {@Id@GeneratedValue(strategy= GenerationType.IDENTITY)private Long id;private String userId;private String visitUrl;private OffsetDateTime createDate;
}package com.penngo.example.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminUser {@Id@GeneratedValue(strategy= GenerationType.IDENTITY)private Long id;private String name;
}
GraphQL對(duì)應(yīng)的輸入類
package com.penngo.example.entity;
import lombok.Data;
import java.time.OffsetDateTime;
@Data
public class AdminLogInput {private String userId;private String visitUrl;private OffsetDateTime createDate;}package com.penngo.example.entity;
import lombok.Data;
@Data
public class AdminUserInput {private String name;
}
對(duì)應(yīng)的數(shù)據(jù)庫操作類
package com.penngo.example.repository;
import com.penngo.example.entity.AdminUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AdminUserRepository extends JpaRepository<AdminUser,Long> {AdminUser findById(String userId);
}package com.penngo.example.repository;
import com.penngo.example.entity.AdminLog;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;public interface AdminLogRepository extends JpaRepository<AdminLog,Long> {List<AdminLog> findAllByUserId(String userId);
}
對(duì)外服務(wù)接口類
package com.penngo.example.controller;
import com.penngo.example.entity.*;
import com.penngo.example.repository.AdminLogRepository;
import com.penngo.example.repository.AdminUserRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;
import java.util.List;@Controller
public class AdminLogController {private final AdminUserRepository adminUserRepository;private final AdminLogRepository adminLogRepository;public AdminLogController(AdminUserRepository adminUserRepository, AdminLogRepository adminLogRepository){this.adminLogRepository = adminLogRepository;this.adminUserRepository = adminUserRepository;}@QueryMappingpublic List<AdminLog> allLogs(){List<AdminLog> logsList = adminLogRepository.findAll();return logsList;}@QueryMappingpublic List<AdminLog> logByUser(@Argument String userid){return adminLogRepository.findAllByUserId(userid);}@SchemaMapping(typeName = "AdminLog" ,field = "user")public AdminUser getAuthor(AdminLog adminLog){AdminUser adminUser = adminUserRepository.findById(adminLog.getUserId());return adminUser;}@MutationMappingpublic AdminLog createLog(@Argument AdminLogInput adminLogInput){AdminLog log = new AdminLog();BeanUtils.copyProperties(adminLogInput,log);return adminLogRepository.save(log);}
}package com.penngo.example.controller;
import com.penngo.example.entity.*;
import com.penngo.example.repository.AdminLogRepository;
import com.penngo.example.repository.AdminUserRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import java.util.List;@Controller
public class AdminUserController {private final AdminUserRepository adminUserRepository;private final AdminLogRepository adminLogRepository;public AdminUserController(AdminUserRepository adminUserRepository, AdminLogRepository adminLogRepository){this.adminLogRepository = adminLogRepository;this.adminUserRepository = adminUserRepository;}@QueryMappingpublic List<AdminLog> userById(@Argument String userid){return adminLogRepository.findAllByUserId(userid);}@MutationMappingpublic AdminUser createUser(@Argument AdminUserInput adminUserInput){AdminUser user = new AdminUser();BeanUtils.copyProperties(adminUserInput,user);return adminUserRepository.save(user);}
}package com.penngo.example.controller;
import com.penngo.example.entity.AdminLog;
import com.penngo.example.repository.AdminLogRepository;
import com.penngo.example.repository.AdminUserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
import org.springframework.stereotype.Controller;
import reactor.core.publisher.Flux;
import java.time.Duration;@Controller
public class GreetingController {private final AdminUserRepository adminUserRepository;private final AdminLogRepository adminLogRepository;public GreetingController(AdminUserRepository adminUserRepository, AdminLogRepository adminLogRepository){this.adminLogRepository = adminLogRepository;this.adminUserRepository = adminUserRepository;}// 數(shù)據(jù)訂閱,取最新的5條數(shù)據(jù),每5秒發(fā)送一條給客戶端,一共5次@SubscriptionMappingpublic Flux<AdminLog> greetings(){System.out.println("greetings====================");Page<AdminLog> logsList = adminLogRepository.findAll(PageRequest.of(0,5).withSort(Sort.Direction.DESC, "id"));return Flux.fromStream(logsList.stream()).delayElements(Duration.ofSeconds(5)).take(5);}
}
自定義日期數(shù)據(jù)類型DateTime
package com.penngo.example.component;
import graphql.scalars.ExtendedScalars;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;@Configuration
public class CustomScalarType {@Beanpublic RuntimeWiringConfigurer runtimeWiringConfigurer() {return wiringBuilder -> wiringBuilder.scalar(ExtendedScalars.DateTime);}
}
服務(wù)啟動(dòng)類
package com.penngo.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class GraphqlAppApplication {public static void main(String[] args) {SpringApplication.run(GraphqlAppApplication.class, args);}
}
3、運(yùn)行效果
3.1、添加用戶
添加用戶
mutation{ createUser(adminUserInput: { name: "test1", } ) { id name } }
3.2、添加日志
添加日志
mutation{createLog(adminLogInput: {userId: "1",visitUrl: "http://localhost:8080/method1"createDate: "2023-09-17T19:39:57+08:00"} ){idvisitUrlcreateDate} }
3.3、查詢所有日志
查詢所有日志
query{allLogs{idvisitUrluser{idname}createDate}
}
3.4、查詢指定用戶日志
查詢指定用戶日志
query{logByUser(userid:"1") {idvisitUrluser{idname}createDate}
}
3.5、數(shù)據(jù)訂閱
數(shù)據(jù)訂閱,訂閱需要有websocket的支持。
subscription {greetings
}
4、總結(jié)
使用Spring for GraphQL試用了GraphQL后,它實(shí)現(xiàn)按需取數(shù)據(jù)的功能。服務(wù)器開發(fā)人員和前端開發(fā)人員可以通過schema.graphqls定義文件,協(xié)定好接口和數(shù)據(jù),省掉寫接口文檔的工作。
缺點(diǎn)可能就是需要一點(diǎn)學(xué)習(xí)成本,雖然提供數(shù)據(jù)嵌套可以通過一個(gè)請求獲取所有數(shù)據(jù),但是嵌套復(fù)雜可能引起性能問題。
Spring for GraphQL官方參考:https://docs.spring.io/spring-graphql/docs/current/reference/html/#overview