SpringBoot与GraphQL集成
目录GraphQL介绍SpringBoot与Graphql集成
运行环境:JDK8,Maven3。0
技术栈:SpringBoot2。5一、GraphQL介绍
GraphQL既是一种用于API的查询语言也是一个满足你数据查询的运行时。GraphQL对你的API中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让API更容易地随着时间推移而演进,还能用于构建强大的开发者工具。
GraphQL优势请求你所要的数据,不多不少
向你的API发出一个GraphQL请求就能准确获得你想要的数据,不多不少。GraphQL查询总是返回可预测的结果。使用GraphQL的应用可以工作得又快又稳,因为控制数据的是应用,而不是服务器。获取多个资源只用一个请求
GraphQL查询不仅能够获得资源的属性,还能沿着资源间引用进一步查询。典型的RESTAPI请求多个资源时得载入多个URL,而GraphQL可以通过一次请求就获取你应用所需的所有数据。这样一来,即使是比较慢的移动网络连接下,使用GraphQL的应用也能表现得足够迅速。描述所有的可能类型系统
GraphQLAPI基于类型和字段的方式进行组织,而非入口端点。你可以通过一个单一入口端点得到你所有的数据能力。GraphQL使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。SpringBoot与GraphQL集成Graphql插件Idea开发工具集成Graphql插件,从FilesettingsPlugins安装插件
安装完插件后,可创建graphql类型文件
数据库及表创建数据库创建:
createdatabasesopbase;表创建:
createtablesysuser
(
useridbigintautoincrement
primarykey,
usernamevarchar(50)notnullcomment用户名,
passwordvarchar(100)nullcomment密码,
saltvarchar(20)nullcomment盐,
emailvarchar(100)nullcomment邮箱,
mobilevarchar(100)nullcomment手机号,
statustinyintnullcomment状态0:禁用1:正常,
deptidbigintnullcomment部门ID,
createtimedatetimenullcomment创建时间,
constraintusername
unique(username)
)
comment系统用户charsetutf8mb4;项目工程结构及源码介绍项目工程结构
核心源码介绍API层提供查询及更改类解析器,分别实现查询和更改接口
用户查询类解析器
authorlxj
see〔相关类方法〕(可选)
since〔产品模块版本〕(可选)
Component
publicclassUserQueryResolverimplementsGraphQLQueryResolver{
Autowired
privateUserServiceuserService;
publicListgetUserList(){
returnuserService。list();
}
publicUsergetUserInfo(LonguserId){
returnuserService。getById(userId);
}
publicPageUtilsgetUserPage(MapString,Objectparams){
PageUtilspageuserService。queryPage(params);
returnpage;
}
}
用户更新类解析器
authorlxj
see〔相关类方法〕(可选)
since〔产品模块版本〕(可选)
Component
publicclassUserMutationResolverimplementsGraphQLQueryResolver,GraphQLMutationResolver{
Autowired
privateUserServiceuserService;
publicRaddUser(Useruser){
userService。stroe(user);
returnR。ok();
}
publicRdeleteUser(IntegeruserId){
if(userIdnull){
returnR。error(1,fail);
}
userService。removeById(userId);
returnR。ok();
}
publicRupdateUser(Useruser){
if(usernull){
returnR。error(1,fail);
}
userService。updateById(user);
returnR。ok();
}
}dao数据操作层DAO
Mapper
表明这是一个Mapper,也可以在启动类上加上包扫描
Mapper继承该接口后,无需编写mapper。xml文件,即可获得CRUD功能
publicinterfaceUserMapperextendsBaseMapper{
}entity实体类
Data
TableName(valuesysuser)
ApiModel(description用户信息)
publicclassUserimplementsSerializable{
privatestaticfinallongserialVersionUID5644799954031156649L;
value与数据库主键列名一致,若实体类属性名与表主键列名一致可省略value
TableId(valueuserid,typeIdType。AUTO)指定自增策略
ApiModelProperty(value用户ID,requiredtrue)
privateLonguserId;
用户名
ApiModelProperty(value用户名,requiredtrue)
privateStringusername;
密码
ApiModelProperty(value用户密码,requiredtrue)
privateStringpassword;
盐
privateStringsalt;
邮箱
ApiModelProperty(value用户邮箱,requiredtrue)
privateStringemail;
手机号
privateStringmobile;
状态0:禁用1:正常
privateIntegerstatus;
部门ID
privateLongdeptId;
部门名称
TableField(existfalse)
privateStringdeptName;
角色ID列表
TableField(existfalse)
privateListroleIdList;
创建时间
TableField(fillFieldFill。INSERTUPDATE)
privateDatecreateTime;
}service业务逻辑层
接口类:
publicinterfaceUserServiceextendsIService{
分页查询
paramparams
return
PageUtilsqueryPage(MapString,Objectparams);
根据姓名查询
paramname
return
UserqueryByName(Stringname);
booleanstroe(Useruser);
voidupdate(Useruser);
UsergetUserById(LonguserId);
}
实现类:
Service(userService)
publicclassUserServiceImplextendsServiceImplUserMapper,UserimplementsUserService{
Override
publicPageUtilsqueryPage(MapString,Objectparams){
Stringname(String)params。get(username);
QueryWrapperuserQueryWrappernewQueryWrapper();
userQueryWrapper。like(StringUtils。isNotEmpty(name),username,name);
QueryquerynewQuery();
IPagepagethis。page(query。getPage(params),userQueryWrapper);
returnnewPageUtils(page);
}
Override
publicUserqueryByName(Stringname){
QueryWrapperuserQueryWrappernewQueryWrapper();
userQueryWrapper。eq(username,name);
returnthis。getOne(userQueryWrapper);
}
Override
Transactional(rollbackForException。class)
publicbooleanstroe(Useruser){
StringsaltRandomStringUtils。randomAlphanumeric(20);
Stringpwduser。getPassword()salt;
user。setSalt(salt);
user。setPassword(DigestUtils。md5Hex(pwd));
this。save(user);
returntrue;
}
Override
Transactional(rollbackForException。class)
publicvoidupdate(Useruser){
if(org。apache。commons。lang。StringUtils。isBlank(user。getPassword())){
user。setPassword(null);
}else{
StringsaltRandomStringUtils。randomAlphanumeric(20);
Stringpwduser。getPassword()salt;
user。setSalt(salt);
user。setPassword(DigestUtils。md5Hex(pwd));
}
this。updateById(user);
}
Override
publicUsergetUserById(LonguserId){
Useruserthis。getById(userId);
returnuser;
}
}Application应用启动类
启动类
authorlxj
SpringBootApplication
publicclassLearnGraphqlApplication{
publicstaticvoidmain(String〔〕args){
SpringApplication。run(LearnGraphqlApplication。class,args);
}
}Mapper配置
lt;?xmlversion1。0encodingUTF8?
mapperPUBLICmybatis。orgDTDMapper3。0ENhttp:mybatis。orgdtdmybatis3mapper。dtd
mappernamespacecom。learn。springboot。dao。UserMapper
resultMaptypecom。learn。springboot。entity。UseriduserMap
resultpropertyuserIdcolumnuserid
resultpropertyusernamecolumnusername
resultpropertypasswordcolumnpassword
resultpropertysaltcolumnsalt
resultpropertyemailcolumnemail
resultpropertymobilecolumnmobile
resultpropertystatuscolumnstatus
resultpropertydeptIdcolumndeptid
resultpropertycreateTimecolumncreatetime
spanresultMap
spanmapperapplication。yml应用配置文件,应用启动会自动读取配置
server:
port:80
servlet:
contextpath:
tomcat:
tomcat的URI编码
uriencoding:UTF8
tomcat最大线程数,默认为200
maxthreads:800
Tomcat启动初始化的线程数,默认值25
minsparethreads:30
spring:
datasource:
type:com。alibaba。druid。pool。DruidDataSource
druid:
driverclassname:com。mysql。cj。jdbc。Driver
username:sopbase
password:sopbase
url:jdbc:mysql:localhost:3306sopbase?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneUTC
initialsize:10连接池初始大小
maxactive:100连接池中最大的活跃连接数
minidle:10连接池中最小的活跃连接数
maxwait:60000配置获取连接等待超时的时间
poolpreparedstatements:true打开PSCache,并且指定每个连接上PSCache的大小
maxpoolpreparedstatementperconnectionsize:20
timebetweenevictionrunsmillis:60000
minevictableidletimemillis:300000
Oracle需要打开注释
validationquery:SELECT1FROMDUAL
testwhileidle:true是否在连接空闲一段时间后检测其可用性
testonborrow:false是否在获得连接后检测其可用性
testonreturn:false是否在连接放回连接池后检测其可用性
statviewservlet:
enabled:true
urlpattern:druid
loginusername:admin
loginpassword:admin
filter:
stat:
logslowsql:true
slowsqlmillis:1000
mergesql:false
wall:
config:
multistatementallow:true
mybatis
mybatisplus:
mapperlocations:classpath:mapper。xml
实体扫描,多个package用逗号或者分号分隔com。example。。entity
typeAliasesPackage:com。learn。springboot。entity
globalconfig:
数据库相关配置
dbconfig:
主键类型AUTO:数据库ID自增,INPUT:用户输入ID,IDWORKER:全局唯一ID(数字类型唯一ID),UUID:全局唯一IDUUID;
idtype:AUTO
logicdeletevalue:1
logicnotdeletevalue:0
banner:false
原生配置
configuration:
开启sql日志
logimpl:org。apache。ibatis。logging。stdout。StdOutImpl
logimpl:org。apache。ibatis。logging。slf4j。Slf4jImpl
logimpl:org。apache。ibatis。logging。log4j2。Log4j2Impl
该配置就是将带有下划线的表字段映射为驼峰格式的实体类属性
mapunderscoretocamelcase:true
cacheenabled:false
callsettersonnulls:true
jdbctypefornull:null
GraphQLcom。graphqljavakickstart
graphql:
servlet:
mapping:graphql
enabled:true
corsEnabled:true关闭跨域,仅使用浏览器插件调试时设置为false
playground:
cdn:
enabled:trueplayground使用cdn的静态文件
ifyouwanttoExceptionHandlerannotationforcustomGraphQLErrors
exceptionhandlersenabled:true
contextSetting:PERREQUESTWITHINSTRUMENTATION
tools:
扫描resource下。graphql后缀的文件
schemalocationpattern:。graphqlGraphQL文件配置
result。graphql
typeR{
code:Int!
msg:String
}
root。graphql
定义查询类型和更改类型
schema{
query:Query
mutation:Mutation
}
定义一个空的查询类型
typeQuery{
}
定义一个空的更改类型
typeMutation{
}
user。graphql
extendtypeQuery{
getUserInfo(userId:String!):User
getUserList:〔User〕
getUserPage(params:paginationUserInput!):PageUserResult!
}
extendtypeMutation{
addUser(user:addUserInput!):R
deleteUser(userId:String!):R
updateUser(user:updateUserInput):R
}
typeUser{
userId:Long
用户名
username:String
密码
password:String
盐
salt:String
邮箱
email:String
手机号
mobile:String
状态0:禁用1:正常
status:Int
部门ID
deptId:Int
createTime:Date
}
typePageUserResult{
currPage:Int!
pageSize:Int!
totalPage:Int!
totalCount:Int!
list:〔User〕!
}
inputpaginationUserInput{
limit:String10
page:String0
查询条件生成不固定
用户名
username:String
密码
password:String
盐
salt:String
邮箱
email:String
手机号
mobile:String
状态0:禁用1:正常
status:Int
部门ID
deptId:Int
}
添加系统用户输入参数
inputaddUserInput{
用户名
username:String
密码
password:String
盐
salt:String
邮箱
email:String
手机号
mobile:String
状态0:禁用1:正常
status:Int
部门ID
deptId:Int
}
更新系统用户输入参数
inputupdateUserInput{
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
groupIdcom。graphqljavakickstartspangroupId
artifactIdgraphqlspringbootstarterspanartifactId
version11。0。0spanversion
spandependency
dependency
groupIdcom。graphqljavakickstartspangroupId
artifactIdgraphiqlspringbootstarterspanartifactId
version8。1。1spanversion
scoperuntimespanscope
spandependencyapplication。yml中graphql配置
GraphQL
graphql:
servlet:
mapping:graphql
enabled:true
corsEnabled:true关闭跨域,仅使用浏览器插件调试时设置为false
playground:
cdn:
enabled:trueplayground使用cdn的静态文件
ifyouwanttoExceptionHandlerannotationforcustomGraphQLErrors
exceptionhandlersenabled:true
contextSetting:PERREQUESTWITHINSTRUMENTATION
tools:
扫描resource下。graphql后缀的文件
schemalocationpattern:。graphql加载Mybatis配置
MapperScan注解加载扫描持久层包路径,来增加配置。该配置也可以加在工程application启动类。无自定配置,只需通过持久层接口加Mapper注解,可不引用MapperScan注解。
MybatisPlus插件加载
authorlxj
Configuration
MapperScan(com。learn。springboot。dao)
publicclassMybatisPlusConfig{
新的分页插件
Bean
publicMybatisPlusInterceptormybatisPlusInterceptor(){
MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();
interceptor。addInnerInterceptor(newPaginationInnerInterceptor(DbType。MYSQL));
returninterceptor;
}
}
数据表字段自动填充实现
定义写数据入库默认值
authorlxj
Component
publicclassCTMetaObjectHandlerimplementsMetaObjectHandler{
Override
publicvoidinsertFill(MetaObjectmetaObject){
this。setFieldValByName(createTime,newDate(),metaObject);
}
Override
publicvoidupdateFill(MetaObjectmetaObject){
自定元数据处理逻辑
}
}自定义graphql类型
定义graphql新Long类型
authorlxj
Configuration
publicclassLongScalarConfig{
Bean
publicGraphQLScalarTypelongScalar(){
returnGraphQLScalarType。newScalar()
。name(Long)
。description(Java8Longasscalar。)
。coercing(newCoercingLong,String(){
Override
publicStringserialize(finalObjectdataFetcherResult){
if(dataFetcherResultinstanceofLong){
returndataFetcherResult。toString();
}else{
thrownewCoercingSerializeException(ExpectedaLongobject。);
}
}
Override
publicLongparseValue(finalObjectinput){
try{
if(inputinstanceofString){
returnnewLong((String)input);
}else{
thrownewCoercingParseValueException(ExpectedaString);
}
}catch(Exceptione){
thrownewCoercingParseValueException(String。format(NotavalidLong:s。,input),e
);
}
}
Override
publicLongparseLiteral(finalObjectinput){
if(inputinstanceofStringValue){
try{
returnnewLong(((StringValue)input)。getValue());
}catch(Exceptione){
thrownewCoercingParseLiteralException(e);
}
}else{
thrownewCoercingParseLiteralException(ExpectedaStringValue。);
}
}
})。build();
}
}工程演示右键运行Application应用启动类的main函数,然后在浏览器访问图形化界面:
http:localhostgraphiql
新增数据调用
查询调用:
注意:typeQuery方法名和我们resolver中的方法名必须一致graphql文件中定义的对象的属性列,必须在java实体类中一一进行对应
喜迎二十大寻美平邑行浚美铜石山水秀,满山都是红山果10月13日上午,平邑县委统战部组织县新联会寻美平邑行采风团会员,到铜石镇开展寻美采风活动。这支由摄影爱好者网红主播作协会员等组成的队伍,一进入该镇的彭泉流域,就被这里的景色所震撼
高虎不务正业只卖土货你们又来收土特产了,欢迎欢迎,快快,屋里坐,我把这几天存的鸡蛋拿出来。一大早,看到身背背篓的高虎等一行人出现,百里杜鹃普底乡红丰村村民杨学飞老人一个劲儿地连声招呼。高虎是普底乡的一
仅一条未被污染的长江,两岸都是毒蛇,一下雨就变成血河说到旅游景点,我相信每个人都去过很多地方,一般来说,人们外出是为了放松或充实自己,所以他们通常选择去一些著名的旅游景点。但过了很长一段时间,他们会觉得这些景点并不有趣,特别是在旅游
二十大新闻中心组织中外记者沿北京中轴线参访活动中国日报北京10月13日电(记者邹红)2022年10月12日下午,中国共产党第二十次全国代表大会新闻中心组织近80名中外媒体记者沿北京中轴线开展揽胜中轴气象,品韵古都风华参观采访活
上海到成都,有一趟直达高铁,全程只有9站上海与成都,一个是东方明珠,一个是天府之国。它们,是我国东部和西部两大重要的城市。两地相距,将近2000多公里。日常往来,除了飞机之外,有没有便捷的高铁直达呢?答案是当然有了!目前
最美贵州,2019贵州游记梵净山(一)梵净山,这是在贵州和黄果树瀑布齐名的旅游胜地,梵净山不仅风景优美,而且在它身上还有许许多多的传说,传说只有有缘人才能一识它的真面目,那么我是不是有缘人呢。。?说明1。本文照片均为j
玩转地理贝加尔湖,中国的北海?北海,中国故土如今的贝加尔湖(baykal)汉名,源自英(俄)语的音译。这个湖泊,在中国古代曾被称作北海(早期的俄罗斯移民,称为圣海),是中国北方部族主要活动地区,如汉武帝时期的苏
湖南一个县,人口不到60万,却有一个好听的名字喜欢旅游的人,对于湖南都不会陌生,这个位于我国华中地区的省份,因丰富的资源和绚烂的风光受到广泛关注,作为国内著名的旅游省份之一,湖南确实有着值得骄傲的地方,单是它的自然风光,就令人
航班信息丨西藏各机场10月14日航班情况尊敬的各位旅客2022年10月14日,西藏各机场计划执行进出港航班13架次,具体如下一拉萨贡嘎机场(一)出港航班航班号西藏航空TV9943拉萨07500950阿里航班号四川航空3U
从城市到野外,来一场沉浸式露营,坦克300的精彩比想象中更多前段时间我来了一场沉浸式露营,当时决定去露营只是突如其来的想法,并没有提前规划行程或是去提前计划车型。好在公司就有现成的露营装备,但我日常开的是一辆轿车,所以驾驶轿车显然不适合去露
闭上眼睛或将会更好,中国女游客去荷兰泡温泉,都经历了什么去国外旅游对于一些人来说是不错的体验各国文化的尝试,而且现在不少人的生活质量与收入都得到了提高,所以很多人其实都拥有了可以出国游玩的机会与条件,只是对于需要去不同国家游玩的人来说,