本文中搭建的项目是在参考company的真实项目基础上简化而来,主要简化了许多业务逻辑、一些数据源(比如OTS redis在项目中的使用以及web权限验证等等,,),项目变得简单清晰,突出使用SpringBoot集成GraphQL服务,并进行流程开发。虽然是简化后的,但是开发架构基本贴近我们工作中的架构环境和架构原则,所以你要是第一次使用Granphql,可以参考本文进行实际开发环境搭建。
1、本次构建的工程环境:
JDK1.8
Spring boot 2.1.6
Spring web Start :构建Web服务,包括RESTful,使用SpringMVC的应用程序,使用ApacheTomcat作为默认的嵌入式容器。
MySQL
Mybatis
Graphql-java
fastjson
Lombok
Maven
2、项目创建方法参考:
IDEA 使用Spring Initializr 构建springboot项目
到下面这一步时,选择以下插件和框架支持:
工程创建完,删除掉自动生成的一些无用的文件、文件夹后,工程目录如下:
1、配置pom文件:
配置启动路径、增加fastjson依赖、增加Graphql-java依赖。
配置完成后pom文件如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
com.aigov
graphql_java
0.0.1-SNAPSHOT
graphql_java
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
com.alibaba
fastjson
1.2.24
com.graphql-java-kickstart
graphql-spring-boot-starter
5.10.0
com.graphql-java-kickstart
graphiql-spring-boot-starter
5.10.0
runtime
com.graphql-java-kickstart
graphql-java-tools
5.5.0
org.springframework.boot
spring-boot-maven-plugin
com.aigov.graphql_java.GraphqlJavaApplication
此处对graphq-java依赖方式参考了源码文件:https://github.com/graphql-java-kickstart/graphql-spring-boot#documentation
但是不是完全照搬,Altair、Voyager 等几个Graphql的可视化工具、插件、IED啥的没有引入。
添加了上述Graphql 部分的依赖后,maven会导入下面几个jar包:
有空我会参考gitHub源码,解释一下这三个依赖包。
2、改application.properties文件为application.yml。配置项目信息:
server:
port: 6001
spring:
datasource:
name: test
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/aigov_core?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: root
password: 123456
graphql:
servlet:
mapping: /graphql
enabled: true
corsEnabled: true
tools:
schemaLocationPattern: "**/*.graphqls"
graphiql:
mapping: /graphiql
endpoint:
graphql: /graphql
subscriptions: /subscriptions
static:
basePath: /
enabled: true
pageTitle: GraphiQL
cdn:
enabled: false
version: 0.11.11
props:
resources:
query: testquery.query
defaultQuery: testquery.query
variables:
editorTheme: "solarized light"
headers:
Authorization: "Bearer vdff3344ffs v"
到这一步,项目基本的资源环境都配置好了。
3、建几个包文件夹,最终包和代码结构如下:
4、mysql建表-车辆信息表(clxx)
在resources文件夹下的granphql包下新建一些 .granphiql 文件,方法如下:
1、在granphql包下面新建query.graphqls文件
query.graphqls里可以包含了你需要的所有查询请求接口,这就是第一节里面讲的Query,所有查询的入口,这里的查询是个宏观概念,实际上我在项目中,增删改查都在这个Query入口下实现了。
clxx:ClxxRoot ——clxx你可以理解为这是一个子查询的入口名字 ,前端所需要的后端对车辆信息(clxx)所做的所有数据处理,都将定义在ClxxRoot里面,比如这里面可以实现查询并返回车辆信息数据给前端的api、、
type Query {
#车辆信息
clxx:ClxxRoot
}
2、在granphql包下新建clxx.graphqls文件
clxx.graphqls里面会实现一些针对车辆信息的增删改查API,同时也会定义一些“对象”,像java一样。比如像下面add API中需要传入一个ClxxVoAdd对象,这时我们需要在.graphqls文件中定义他。
type ClxxRoot{
#新增 cldm必传
add(clxx:ClxxVoAdd):Result
#删除 根据车牌号删除
delete(cph:String!):Result
#修改 cldm必传
edit(clxx:ClxxVoEdit):Result
#查询 根据cph模糊查询
find(cph:String!):[ClxxVo]
}
input ClxxVoAdd{
#车辆代码
cldm:String!
#车牌号
cph:String
#车牌颜色
cpys:Int
#车辆vin码
vin:String
}
input ClxxVoEdit{
#车辆代码
cldm:String!
#车牌号
cph:String
#车牌颜色
cpys:Int
#车辆vin码
vin:String
}
type ClxxVo{
#车辆代码
cldm:String
#车牌号
cph:String
#车牌颜色
cpys:Int
#车辆vin码
vin:String
}
3、定义特殊graphql对象
上面这个graphql文件中有个 Result 对象,上面没有提及,并不是因为它是graphql的自带对象,实际上这是需要我们自己根据自己需求定义的,这里将用到枚举,是的graphql也是支持枚举的。
新建一个scalar.graphqls文件,定义一个Result响应对象:
type Result{
result: ResultCode!
msg:String
}
enum ResultCode {
#成功
success
#失败
fail
#异常
error
}
到这里graphql的描述文件(也就是schema)差不多写完了,后面就要需要把schema里面定义过的graphql对象在java文件里定义成java对象。
4、定义java bean对象
这里的bean对象就是对应上面graphql对象的。
package com.aigov.graphql_java.entity;
import lombok.Data;
/**
* @author : aigoV
* @date :2019/8/19
* 车辆信息新增vo对象
**/
@Data
public class ClxxVoAdd {
/** 车辆代码 **/
private String cldm;
/** 车牌号 **/
private String cph;
/** 车牌颜色 **/
private Integer cpys;
/** 车辆vin **/
private String vin;
}
4.2 创建 ClxxVoEdit.java
package com.aigov.graphql_java.entity;
import lombok.Data;
/**
* @author : aigoV
* @date :2019/8/19
* 车辆信息修改对象
**/
@Data
public class ClxxVoEdit {
/** 车辆代码 **/
private String cldm;
/** 车牌号 **/
private String cph;
/** 车牌颜色 **/
private Integer cpys;
/** 车辆vin码 **/
private String vin;
}
4.3 ClxxVo.java
package com.aigov.graphql_java.entity;
import lombok.Data;
/**
* @author : aigoV
* @date :2019/8/19
* 车辆信息bean对象
**/
@Data
public class ClxxVo {
/** 车辆代码 **/
private String cldm;
/** 车牌号 **/
private String cph;
/** 车牌颜色 **/
private Integer cpys;
/** 车辆vin码 **/
private String vin;
}
4.4 定义scalar.graphqls文件中的特殊 Result 对象
package com.aigov.graphql_java.entity;
/**
* @author : aigoV
* @date :2019/8/26
* ResultCode 枚举
**/
public enum ResultCodeEnum {
success,
fail,
error
}
package com.aigov.graphql_java.entity;
/**
* @author : aigoV
* @date :2019/8/26
* 接口调用结果
**/
public class ResultScalar {
private ResultScalar(){ }
private ResultCodeEnum result;
private String msg;
public static ResultScalar error(String msg){
ResultScalar rs = new ResultScalar();
rs.result = ResultCodeEnum.error;
rs.setMsg(msg);
return rs;
}
public static ResultScalar error(){
ResultScalar rs = new ResultScalar();
rs.result = ResultCodeEnum.error;
rs.setMsg("程序内部错误");
return rs;
}
public static ResultScalar success(String msg){
ResultScalar rs = new ResultScalar();
rs.result = ResultCodeEnum.success;
rs.setMsg(msg);
return rs;
}
public static ResultScalar fail(String msg){
ResultScalar rs = new ResultScalar();
rs.result = ResultCodeEnum.fail;
rs.setMsg(msg);
return rs;
}
public ResultCodeEnum getResult() {
return result;
}
public void setResult(ResultCodeEnum result) {
this.result = result;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
5、编写业务方法 (以增加clxx数据这个接口为例,其他小接口参照即可。)
package com.aigov.graphql_java.mapper;
import com.aigov.graphql_java.entity.ClxxVoAdd;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
/**
* @author : aigoV
* @date :2019/8/26
* 车辆信息mapper
**/
@Mapper
public interface ClxxMapper {
@Insert("INSERT INTO aigov_core.clxx (cldm,cph,cpys,vin) " +
"VALUES(#{cldm,jdbcType=VARCHAR},#{cph,jdbcType=VARCHAR},#{cpys,jdbcType=INTEGER},#{vin,jdbcType=VARCHAR})")
int addClxx(ClxxVoAdd clxxVoAdd); //新增车辆信息
}
package com.aigov.graphql_java.service;
import com.aigov.graphql_java.dataload.DoResultError;
import com.aigov.graphql_java.entity.ClxxVoAdd;
import com.aigov.graphql_java.entity.ResultScalar;
import com.aigov.graphql_java.mapper.ClxxMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author : aigoV
* @date :2019/8/26
* clxx业务处理
**/
@Service
public class clxxServer {
@Autowired
ClxxMapper clxxMapper;
//@DoResultError("clxx数据插入失败啦!")
@Transactional
public ResultScalar addClxx(ClxxVoAdd clxxVoAdd){
clxxMapper.addClxx(clxxVoAdd);
return ResultScalar.success("车辆信息数据已插入成功!");
}
}
6、编写解析器Resolver
解释一下,由于业务逻辑简单,这里的解析器代码逻辑也简单,但是实际开发中,我们的解析器可能需要更复杂的逻辑代码来实现复杂需求,比如下面那个def对象会被利用起来,这个有时间我再把复杂点的解析器需求加在这篇博文里。
package com.aigov.graphql_java.resolver;
import com.aigov.graphql_java.entity.ClxxVoAdd;
import com.aigov.graphql_java.entity.ResultScalar;
import com.aigov.graphql_java.service.ClxxServer;
import com.coxautodev.graphql.tools.GraphQLResolver;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author : aigoV
* @date :2019/8/26
* clxx解析器
**/
public class ClxxResolver implements GraphQLResolver {
@Autowired
ClxxServer clxxServer;
public ResultScalar add (ClxxVoAdd clxxVoAdd,DataFetchingEnvironment dfe){
return clxxServer.addClxx(clxxVoAdd);
}
}
7、最后浏览器端借助graphiql 测试
好了,一个Spring boot集成Graphql实现前后端分离的实际业务过程和项目构造大致就是这样,当然这是一个简单的业务举例,实际开发中还会遇到更多需求,更复杂逻辑,比如如何在前端申请介入graphql api前验证web 用户权限等等。。