当前位置:科学 > 正文

SpringBoot 与GraphQL集成_当前热讯

2023-03-13 11:53:37  来源:小白程序员进阶之路

目录

GraphQL介绍Spring Boot与Graphql集成

运行环境:JDK 8,Maven 3.0+

技术栈:SpringBoot 2.5+


(资料图)

一、GraphQL 介绍

GraphQL既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

GraphQL优势

请求你所要的数据,不多不少

向你的 API 发出一个GraphQL 请求就能准确获得你想要的数据,不多不少。 GraphQL 查询总是返回可预测的结果。使用 GraphQL 的应用可以工作得又快又稳,因为控制数据的是应用,而不是服务器。

获取多个资源只用一个请求

GraphQL 查询不仅能够获得资源的属性,还能沿着资源间引用进一步查询。典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。这样一来,即使是比较慢的移动网络连接下,使用 GraphQL 的应用也能表现得足够迅速。

描述所有的可能类型系统

GraphQL API 基于类型和字段的方式进行组织,而非入口端点。你可以通过一个单一入口端点得到你所有的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。

Spring Boot与 GraphQL集成

Graphql插件

Idea开发工具集成Graphql插件,从File ->settings –>Plugins安装插件安装完插件后,可创建graphql类型文件

数据库及表创建

数据库创建:

create databasesopbase;

表创建:

create table sys_user
(
user_id
bigint auto_increment
primary key
,
username
varchar(50) not null comment "用户名",
password varchar(100) null comment "密码",
salt
varchar(20) null comment "盐",
email
varchar(100) null comment "邮箱",
mobile
varchar(100) null comment "手机号",
status tinyint null comment "状态 0:禁用 1:正常",
dept_id
bigint null comment "部门ID",
create_time
datetime null comment "创建时间",
constraint username
unique (username)
)
comment "系统用户" charset = utf8mb4;

项目工程结构及源码介绍

项目工程结构

核心源码介绍

API层提供查询及更改类解析器,分别实现查询和更改接口


/**
* 用户查询类解析器
*
*
@author lxj
*
@see [相关类/方法](可选)
*
@since [产品/模块版本] (可选)
*/
@Component
public class UserQueryResolver implements GraphQLQueryResolver {
@Autowired
private UserService userService;
public List<User> getUserList() {
return userService.list();
}
public User getUserInfo(Long userId) {
return userService.getById(userId);
}
public PageUtils<User> getUserPage(Map<String, Object> params){
PageUtils<User> page =
userService.queryPage(params);
return page;
}
}


/**
* 用户更新类解析器
*
*
@author lxj
*
@see [相关类/方法](可选)
*
@since [产品/模块版本] (可选)
*/
@Component
public class UserMutationResolver implements GraphQLQueryResolver, GraphQLMutationResolver {
@Autowired
private UserService userService;
public R addUser(User user) {
userService.stroe(user);
return R.ok();
}
public R deleteUser(Integer userId) {
if(userId ==null){
return R.error(-1,"fail");
}
userService.removeById(userId);
return R.ok();
}
public R updateUser(User user) {
if(user ==null){
return R.error(-1,"fail");
}
userService.updateById(user);
return R.ok();
}
}

dao – 数据操作层 DAO

@Mapper
//表明这是一个Mapper,也可以在启动类上加上包扫描
//Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
public interface UserMapper extends BaseMapper<User> {
}

entity – 实体类

@Data
@TableName
(value = "sys_user")
@ApiModel(description = "用户信息")
public class User implements Serializable {
private static final long serialVersionUID = -5644799954031156649L;
//value与数据库主键列名一致,若实体类属性名与表主键列名一致可省略value
@TableId(value = "user_id", type = IdType.AUTO)//指定自增策略
@ApiModelProperty(value = "用户ID",required=true)
private Long userId;
/**
* 用户名
*/
@ApiModelProperty(value = "用户名",required=true)
private String username;
/**
* 密码
*/
@ApiModelProperty(value = "用户密码",required=true)
private String password;
/**
* 盐
*/
private String salt;
/**
* 邮箱
*/
@ApiModelProperty(value = "用户邮箱",required=true)
private String email;
/**
* 手机号
*/
private String mobile;
/**
* 状态 0:禁用 1:正常
*/
private Integer status;
/**
* 部门ID
*/
private Long deptId;
/**
* 部门名称
*/
@TableField(exist=false)
private String deptName;
/**
* 角色ID列表
*/
@TableField(exist=false)
private List<Long> roleIdList;
/**
* 创建时间
*/
@TableField(fill= FieldFill.INSERT_UPDATE)
private Date createTime;
}

service – 业务逻辑层

接口类:

public interface UserService extends IService<User> {
/**
* 分页查询
*
@param params
* @return
*/
PageUtils queryPage(Map<String, Object> params);
/**
* 根据姓名查询
*
@param name
* @return
*/
User queryByName(String name);
boolean stroe(User user);
void update(User user);
User getUserById(Long userId);
}

实现类:

@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
String name = (String)params.get(
"username");
QueryWrapper<User> userQueryWrapper =
new QueryWrapper<>();
userQueryWrapper.like(StringUtils.
isNotEmpty(name),"username",name);
Query<User> query =
new Query<>();
IPage<User> page=
this.page(query.getPage(params),userQueryWrapper);
return new PageUtils(page);
}
@Override
public User queryByName(String name) {
QueryWrapper<User> userQueryWrapper =
new QueryWrapper<>();
userQueryWrapper.eq(
"username",name);
return this.getOne(userQueryWrapper);
}
@Override
@Transactional
(rollbackFor = Exception.class)
public boolean stroe(User user) {
String salt = RandomStringUtils.
randomAlphanumeric(20);
String pwd = user.getPassword()+salt;
user.setSalt(salt);
user.setPassword(DigestUtils.
md5Hex(pwd));
this.save(user);
return true;
}
@Override
@Transactional
(rollbackFor = Exception.class)
public void update(User user) {
if(org.apache.commons.lang.StringUtils.isBlank(user.getPassword())){
user.setPassword(
null);
}
else{
String salt = RandomStringUtils.
randomAlphanumeric(20);
String pwd = user.getPassword()+salt;
user.setSalt(salt);
user.setPassword(DigestUtils.
md5Hex(pwd));
}
this.updateById(user);
}
@Override
public User getUserById(Long userId) {
User user =
this.getById(userId);
return user;
}
}

Application – 应用启动类

/**
* 启动类
*
*
@author lxj
*/
@SpringBootApplication
public class LearnGraphqlApplication {
public static void main(String[] args) {
SpringApplication.
run(LearnGraphqlApplication.class, args);
}
}

Mapper配置

<?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.learn.springboot.dao.UserMapper">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.learn.springboot.entity.User" id="userMap">
<
result property="userId" column="user_id"/>
<
result property="username" column="username"/>
<
result property="password" column="password"/>
<
result property="salt" column="salt"/>
<
result property="email" column="email"/>
<
result property="mobile" column="mobile"/>
<
result property="status" column="status"/>
<
result property="deptId" column="dept_id"/>
<
result property="createTime" column="create_time"/>
</
resultMap>
</
mapper>

application.yml – 应用配置文件,应用启动会自动读取配置

server:
port: 80
servlet:
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# tomcat最大线程数,默认为200
max-threads: 800
# Tomcat启动初始化的线程数,默认值25
min-spare-threads: 30
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
username: sopbase
password: sopbase
url: jdbc:mysql://localhost:3306/sopbase?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
initial-size: 10 #连接池初始大小
max-active: 100 #连接池中最大的活跃连接数
min-idle: 10 #连接池中最小的活跃连接数
max-wait: 60000 #配置获取连接等待超时的时间
pool-prepared-statements: true #打开PSCache,并且指定每个连接上PSCache的大小
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
#Oracle需要打开注释
#validation-query: SELECT 1 FROM DUAL
test-while-idle: true #是否在连接空闲一段时间后检测其可用性
test-on-borrow: false #是否在获得连接后检测其可用性
test-on-return: false #是否在连接放回连接池后检测其可用性
stat-view-servlet:
enabled: true
url-pattern
: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis
: 1000
merge-sql: false
wall
:
config:
multi-statement-allow: true
#mybatis
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
#实体扫描,多个package用逗号或者分号分隔 com.example.*.entity
typeAliasesPackage: com.learn.springboot.entity
global-config:
#数据库相关配置
db-config:
#主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: AUTO
logic-delete-value: -1
logic-not-delete-value: 0
banner: false
#原生配置
configuration:
#开启sql日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
#log-impl: org.apache.ibatis.logging.log4j2.Log4j2Impl
# 该配置就是将带有下划线的表字段映射为驼峰格式的实体类属性
map-underscore-to-camel-case: true
cache-enabled
: false
call-setters-on-nulls
: true
jdbc-type-for-null
: "null"
#GraphQL com.graphql-java-kickstart
graphql:
servlet:
mapping: /graphql
enabled: true
corsEnabled
: true # 关闭跨域,仅使用浏览器插件调试时设置为false
playground:
cdn:
enabled: true # playground 使用 cdn 的静态文件
# if you want to @ExceptionHandler annotation for custom GraphQLErrors
exception-handlers-enabled: true
contextSetting
: PER_REQUEST_WITH_INSTRUMENTATION
tools:
#扫描 resource 下 .graphql 后缀的文件
schema-location-pattern: "**/*.graphql"

GraphQL文件配置

result.graphql

type R{
code: Int!
msg: String
}

root.graphql

#定义查询类型和更改类型
schema {
query: Query
mutation: Mutation
}
# 定义一个空的查询类型
type Query{
}
# 定义一个空的更改类型
type Mutation{
}

user.graphql

extend type Query {
getUserInfo(userId: String!): User
getUserList: [User]
getUserPage(params : paginationUserInput!): PageUserResult!
}
extend type Mutation {
addUser( user: addUserInput!): R
deleteUser(userId: String!): R
updateUser(user:updateUserInput): R
}
type User {
#
userId: Long
#用户名
username: String
#密码
password: String
#盐
salt: String
#邮箱
email: String
#手机号
mobile: String
#状态 0:禁用 1:正常
status: Int
#部门ID
deptId: Int
#createTime:Date
}
type PageUserResult {
currPage: Int!
pageSize: Int!
totalPage: Int!
totalCount: Int!
list: [User]!
}
input paginationUserInput {
limit: String = "10"
page
: String = "0"
#查询条件生成不固定
#用户名
username: String
#密码
password: String
#盐
salt: String
#邮箱
email: String
#手机号
mobile: String
#状态 0:禁用 1:正常
status: Int
#部门ID
deptId: Int
}
# 添加系统用户输入参数
input addUserInput {
#用户名
username: String
#密码
password: String
#盐
# salt: String
#邮箱
email: String
#手机号
mobile: String
#状态 0:禁用 1:正常
status: Int
#部门ID
deptId: Int
}
# 更新系统用户输入参数
input updateUserInput {
#
userId: String
#用户名
username: String
#密码
password: String
#盐
# salt: String
#邮箱
email: String
#手机号
mobile: String
#状态 0:禁用 1:正常
status: Int
#部门ID
deptId: Int
}


通常是一个对象就是一个 java 实体类,在 graphql 中也如此,也是一个对象对应一个 .graphql 文件。

项目配置说明

项目pom.xml添加graphql依赖

<dependency>
<
groupId>com.graphql-java-kickstart</groupId>
<
artifactId>graphql-spring-boot-starter</artifactId>
<
version>11.0.0</version>
</
dependency>
<
dependency>
<
groupId>com.graphql-java-kickstart</groupId>
<
artifactId>graphiql-spring-boot-starter</artifactId>
<
version>8.1.1</version>
<
scope>runtime</scope>
</
dependency>

application.yml中graphql配置

#GraphQL

graphql:
servlet:
mapping: /graphql
enabled: true
corsEnabled
: true # 关闭跨域,仅使用浏览器插件调试时设置为false
playground:
cdn:
enabled: true # playground 使用 cdn 的静态文件
# if you want to @ExceptionHandler annotation for custom GraphQLErrors
exception-handlers-enabled: true
contextSetting
: PER_REQUEST_WITH_INSTRUMENTATION
tools:
#扫描 resource 下 .graphql 后缀的文件
schema-location-pattern: "**/*.graphql"

加载Mybatis配置

MapperScan 注解加载扫描持久层包路径,来增加配置。该配置也可以加在工程application启动类。无自定配置,只需通过持久层接口加@Mapper注解,可不引用@MapperScan注解。

/**
* MybatisPlus插件加载
*
*
@author lxj
*/
@Configuration
@MapperScan
("com.learn.springboot.dao")
public class MybatisPlusConfig {
/**
* 新的分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor =
new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(
new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

数据表字段自动填充实现

/**
* 定义写数据入库默认值
*
*
@author lxj
*/
@Component
public class CTMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
//自定元数据处理逻辑
}
}

自定义graphql类型

/**
* 定义graphql新Long类型
*
*
@author lxj
*/
@Configuration
public class LongScalarConfig {
@Bean
public GraphQLScalarType longScalar() {
return GraphQLScalarType.newScalar()
.name(
"Long")
.description(
"Java 8 Long as scalar.")
.coercing(
new Coercing<Long, String>() {
@Override
public String serialize(final Object dataFetcherResult) {
if (dataFetcherResult instanceof Long) {
return dataFetcherResult.toString();
}
else {
throw new CoercingSerializeException("Expected a Long object.");
}
}
@Override
public Long parseValue(final Object input) {
try {
if (input instanceof String) {
return new Long((String) input);
}
else {
throw new CoercingParseValueException("Expected a String");
}
}
catch (Exception e) {
throw new CoercingParseValueException(String.format("Not a valid Long: "%s".", input), e
);
}
}
@Override
public Long parseLiteral(final Object input) {
if (input instanceof StringValue) {
try {
return new Long(((StringValue) input).getValue());
}
catch (Exception e) {
throw new CoercingParseLiteralException(e);
}
}
else {
throw new CoercingParseLiteralException("Expected a StringValue.");
}
}
}).build();
}
}

工程演示

右键运行 Application 应用启动类的 main 函数,然后在浏览器访问图形化界面:

http://localhost/graphiql

新增数据调用

查询调用:

注意:

type Query 方法名和我们 resolver 中的方法名必须一致graphql 文件中定义的对象的属性列,必须在 java 实体类中一一进行对应

关键词:

推荐阅读

北京上空现三个太阳 古代幻日现象预兆什么?

北京上空现三个太阳北京上空现三个太阳 专家释疑今日登上热搜,主要是在12月29日有网友拍到北京上空出现了三个太阳。对于这一现象气象专家 【详细】

十大名车车标 世界十大名车车标简介

十大名车车标 世界十大名车车标简介很多爱车人士对于车标是十分熟悉的,基本可以做到看一眼就知道是哪个品牌的车,世界名车更是如此,许多 【详细】

塑料袋属于什么 四种垃圾分类简介

塑料袋属于什么塑料袋是干垃圾。湿垃圾是指易腐烂的垃圾,通常是厨房垃圾。塑料袋不容易腐烂降解,是干垃圾。就是我们常说的白色污染,所以 【详细】

特斯拉的最低价是多少? 其他车型的最低价格是多少?

特斯拉作为一个豪华电动车品牌,你知道特斯拉价格多少钱一辆吗?目前特斯拉销售的主要Model S、Model X以及国产Model 3,那么,特拉斯最 【详细】

通用设备介绍 通用设备包括什么?

通用设备介绍一、通用设备。办公和商务通用设备,包括文化办公机械、消防设备、电机、变压器、锅炉、空调设备、清洁卫生设备、通讯设备、视 【详细】

关于我们  |  联系方式  |  免责条款  |  招聘信息  |  广告服务  |  帮助中心

联系我们:85 572 98@qq.com备案号:粤ICP备18023326号-40

科技资讯网 版权所有