本文中搭建的项目是在参考company的真实项目基础上简化而来,主要简化了许多业务逻辑、一些数据源(比如OTS redis在项目中的使用以及web权限验证等等,,),项目变得简单清晰,突出使用SpringBoot集成GraphQL服务,并进行流程开发。虽然是简化后的,但是开发架构基本贴近我们工作中的架构环境和架构原则,所以你要是第一次使用Granphql,可以参考本文进行实际开发环境搭建。
一、创建一个Spring boot 工程
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文件如下:
<?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 http://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>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aigov</groupId>
<artifactId>graphql_java</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>graphql_java</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--增加json依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<!-- 添加Graphql依赖 -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.10.0</version>
</dependency>
<!-- 添加 GraphiQL 依赖 -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>5.10.0</version>
<scope>runtime</scope>
</dependency>
<!-- 添加 graphql-java-tools 软件包 -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--配置启动路径-->
<configuration>
<mainClass>com.aigov.graphql_java.GraphqlJavaApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
此处对graphq-java依赖方式参考了源码文件:https://github.com/graphql-java-kickstart/graphql-spring-boot#documentation
但是不是完全照搬,Altair、Voyager 等几个Graphql的可视化工具、插件、IED啥的没有引入。
添加了上述Graphql 部分的依赖后,maven会导入下面几个jar包:
- graphiql-spring-boot-autoconfigure: 开发者工具graphiql的自动配置
- graphiql-spring-boot-starter: 开发者工具graphiql的实现
- graphql-java: graphql的java实现
- graphql-java-servlet: 封装graphql服务为servlet,处理graphql的request和response
- graphql-java-tools: 自动加载*.graphqls文件,并屏蔽graphql-java的底层实现细节
- graphql-spring-boot-autoconfigure: graphql-spring-boot的自动配置
- graphql-spring-boot-starter: starter
有空我会参考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)
三、使用Granphql api 实现前后端分离
在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文件中定义他。
- 输入对象定义格式为 input xxxx。
- 返回值对象定义格式为 type xxxx。
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对象的。
- 4.1 创建ClxxVoAdd.java
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 对象
- 4.4.1 创建一个java文件定义 ResultCode 枚举对象
package com.aigov.graphql_java.entity;
/**
* @author : aigoV
* @date :2019/8/26
* ResultCode 枚举
**/
public enum ResultCodeEnum {
success,
fail,
error
}
- 4.4.2 创建一个java文件,定义Result java对象
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数据这个接口为例,其他小接口参照即可。)
- 5.1 定义clxx mapper 操作数据库
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); //新增车辆信息
}
- 5.2 service层调用mapper方法
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 用户权限等等。。