Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
springboot集成gateway_GraphQL视频教程,希望能够帮助你!!!。
目录
运行环境:JDK 8,Maven 3.0+
技术栈:SpringBoot 2.5+
GraphQL既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。
GraphQL优势
向你的 API 发出一个GraphQL 请求就能准确获得你想要的数据,不多不少。 GraphQL 查询总是返回可预测的结果。使用 GraphQL 的应用可以工作得又快又稳,因为控制数据的是应用,而不是服务器。
GraphQL 查询不仅能够获得资源的属性,还能沿着资源间引用进一步查询。典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。这样一来,即使是比较慢的移动网络连接下,使用 GraphQL 的应用也能表现得足够迅速。
GraphQL API 基于类型和字段的方式进行组织,而非入口端点。你可以通过一个单一入口端点得到你所有的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。
create database sopbase;
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;
/**
* 用户查询类解析器
*
* @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();
}
}
@Mapper
//表明这是一个Mapper,也可以在启动类上加上包扫描
//Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
public interface UserMapper extends BaseMapper<User> {
}
@Data
@TableName(value = "sys_user")
@ApiModel(description = "用户信息")
public class User implements Serializable {
private static final long serialVersionUID = -L;
//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;
}
接口类:
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;
}
}
/**
* 启动类
*
* @author lxj
*/
@SpringBootApplication
public class LearnGraphqlApplication {
public static void main(String[] args) {
SpringApplication.run(LearnGraphqlApplication.class, args);
}
}
<?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>
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:
#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"
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 文件。
<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>
#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"
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新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();
}
}
http://localhost/graphiql
新增数据调用
查询调用:
注意:
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章