嗯,前端会一些后台的开发很是重要......
诺,后端如果会一点前端,也同样重要......
GraphQL
【后端同学】我们通常会写很多API,特别是业务成长期,产品希望保障良好的体验的时候,他们会要求后端组合一堆API来满足页面的刚刚好的要素需求。特别是如下图橙色部分的组合API,真的是一个噩梦。前端同学与后端同学一直会不停地纠结。
看我72般变化
【前端同学】我们通常会调用很多API,哪怕只是增加一个要素,我也要多调用一次接口。没办法页面要,后端有(无语的是他们分了好多个接口,有时候鉴权还不一样)。你说就这样,页面的体验,效率能怨我么?
前端开发的烦恼
于是架构师来了,我们做个组合,做个读写模型分析,抽取共性,做到需要的数据就触发查询,再对数据进行编排组合来满足前端的诉求。把大家拉来评审下,避免API风暴......
但,等等,是否应该做一个通用查询,如这样?
统一组合查询API
按需触发接口,组合数据供页面使用,这就是GraphQL面对的问题域。我识别GraphQL的特点如下:
- 立足于从产品经理角度看问题,某种意义上是为前端开发解决问题
- 提供了强大的语法来解释嵌套数据结果,基于强类型,提供了相应的一些质量保障
- 语言支持丰富,如下图
GraphQL支持的语言
其他不说了,按如下步骤,体验下Java的先。
pom.xml这里是基于IDEA社区办,构建的一个Spring Cloud Alibaba web应用。 这里是使用JPA提供数据,依赖的组件列表如下:
org.springframework.boot
spring-boot-starter-web
com.graphql-java
graphql-spring-boot-starter
5.0.2
com.graphql-java
graphql-java-tools
5.2.4
org.projectlombok
lombok
com.graphql-java-kickstart
graphiql-spring-boot-starter
7.1.0
org.springframework.boot
spring-boot-starter-data-jpa
2.4.0
org.springframework.boot
spring-boot-starter-data-rest
com.alibaba
druid
1.1.22
mysql
mysql-connector-java
application.yml
# 应用服务 WEB 访问端口
server:
port: 8080
spring:
application:
name: graphql_demo
#jpa
jpa:
properties:
hibernate:
#dialect: org.hibernate.dialect.MySQL5Dialect
hbm2ddl:
auto: "update"
show-sql: true
#mysql
datasource:
url: "jdbc:mysql://www.my-soft.net.cn:3306/db_nav?useUnicode=true&characterEncoding=UTF-8"
username: "user"
password: "password"
driver-class-name: "com.mysql.cj.jdbc.Driver"
#druid
# 主数据源,默认的
type: "com.alibaba.druid.pool.DruidDataSource"
Entity
Hanzi
package com.example.demo.entity;
import lombok.Builder;
import lombok.Data;
import javax.persistence.*;
@Entity(name = "t_hanzi")
@Data
//@Builder
public class Hanzi {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="f_id",nullable=false)
private Integer id;
@Column(name="f_hanzi",nullable=false)
private String hanzi;
@Column(name="f_pinyin",nullable=false)
private String pinyin;
@Column(name="f_sound_url",nullable=false)
private String soundUrl;
@Column(name="f_meaning",nullable=false)
private String meaning;
@Column(name="f_example",nullable=false)
private String example;
@Column(name="f_create_time",nullable=false)
private String createTime;
@Column(name="f_modify_time",nullable=false)
private String modifyTime;
@Column(name="f_userid_create",nullable=false)
private String useridCreate;
@Column(name="f_userid_modify",nullable=false)
private String useridModify;
@Column(name="f_writing_url",nullable=false)
private String writingUrl;
}
Ciyu
package com.example.demo.entity;
import lombok.Builder;
import lombok.Data;
import javax.persistence.*;
@Entity(name = "t_ciyu")
@Data
//@Builder
public class Ciyu {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="f_id",nullable=false)
private Integer id;
@Column(name="f_ciyu",nullable=false)
private String ciyu;
@Column(name="f_pinyin",nullable=false)
private String pinyin;
@Column(name="f_sound_url",nullable=false)
private String soundUrl;
@Column(name="f_meaning",nullable=false)
private String meaning;
@Column(name="f_example",nullable=false)
private String example;
@Column(name="f_create_time",nullable=false)
private String createTime;
@Column(name="f_modify_time",nullable=false)
private String modifyTime;
@Column(name="f_userid_create",nullable=false)
private String useridCreate;
@Column(name="f_userid_modify",nullable=false)
private String useridModify;
@Column(name="f_synonyms",nullable=false)
private String synonyms;
@Column(name="f_antonyms",nullable=false)
private String antonyms;
@Column(name="f_state",nullable=false)
private Integer state;
}
repository
HanziRepository
package com.example.demo.entity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface HanziRepository extends JpaRepository{ // @Query("from Hanzi d where d.hanzi=':hz' ") public List findByHanziContains(@Param("hz") String hz); }
CiyuRepository
package com.example.demo.entity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; //@Repository public interface CiyuRepository extends JpaRepositoryResolver{ // @Query("from Ciyu d where d.ciyu like '%:hz%' ") public List findByCiyuContains(@Param("hz") String hz); }
package com.example.demo.svc;
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
import com.example.demo.data.Book;
import com.example.demo.data.HanziCiyu;
import com.example.demo.entity.Ciyu;
import com.example.demo.entity.CiyuRepository;
import com.example.demo.entity.Hanzi;
import com.example.demo.entity.HanziRepository;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Component
@AllArgsConstructor
public class HzcyQueryResolver implements GraphQLQueryResolver {
//
}
resource/graphql/*
root.graphqls
type Query{
findHzcy(hz:String !) : HanziCiyu
findHz(hz:String !) : Hanzi
findCy(hz:String !) : [Ciyu]
}
schema.graphqls
type Hanzi{
id : Int
hanzi : String
pinyin : String
soundUrl : String
meaning : String
example : String
createTime : String
modifyTime : String
useridCreate : String
useridModify : String
writingUrl : String
}
type Ciyu{
id : Int
ciyu : String
pinyin : String
soundUrl : String
meaning : String
example : String
createTime : String
modifyTime : String
useridCreate : String
useridModify : String
synonyms : String
antonyms : String
state : Int
}
type HanziCiyu{
hz : Hanzi
lstCiyu : [Ciyu]
}
运行
http://127.0.0.1:8080/graphiql
可看到如下页面,直接输入想要调用的接口与需要接口给出的字段就行。
当然,前台使用时,应该用这个URL :
http://127.0.0.1:8080/graphql?query=
正式使用
这里用到了restful jpa,你也可以如此查询
http://127.0.0.1:8080/hanzis
如此这个图就成立了
理想的结构



