嗯,前端会一些后台的开发很是重要......
诺,后端如果会一点前端,也同样重要......
?
GraphQL
【后端同学】我们通常会写很多API,特别是业务成长期,产品希望保障良好的体验的时候,他们会要求后端组合一堆API来满足页面的刚刚好的要素需求。特别是如下图橙色部分的组合API,真的是一个噩梦。前端同学与后端同学一直会不停地纠结。
?
看我72般变化
【前端同学】我们通常会调用很多API,哪怕只是增加一个要素,我也要多调用一次接口。没办法页面要,后端有(无语的是他们分了好多个接口,有时候鉴权还不一样)。你说就这样,页面的体验,效率能怨我么?
?
前端开发的烦恼
于是架构师来了,我们做个组合,做个读写模型分析,抽取共性,做到需要的数据就触发查询,再对数据进行编排组合来满足前端的诉求。把大家拉来评审下,避免API风暴......
但,等等,是否应该做一个通用查询,如这样?
?
统一组合查询API
按需触发接口,组合数据供页面使用,这就是GraphQL面对的问题域。我识别GraphQL的特点如下:
- 立足于从产品经理角度看问题,某种意义上是为前端开发解决问题
- 提供了强大的语法来解释嵌套数据结果,基于强类型,提供了相应的一些质量保障
- 语言支持丰富,如下图
?
GraphQL支持的语言
其他不说了,按如下步骤,体验下Java的先。
pom.xml
这里是基于IDEA社区办,构建的一个Spring Cloud Alibaba web应用。 这里是使用JPA提供数据,依赖的组件列表如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.graphql-java/graphql-spring-boot-starter -->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.graphql-java/graphql-java-tools -->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
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<Hanzi,Integer> {
// @Query("from Hanzi d where d.hanzi=':hz' ")
public List<Hanzi> 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 JpaRepository<Ciyu,Integer> {
// @Query("from Ciyu d where d.ciyu like '%:hz%' ")
public List<Ciyu> findByCiyuContains(@Param("hz") String hz);
}
Resolver
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
HanziRepository objHanziRepository;
@Resource
CiyuRepository objCiyuRepository;
public Hanzi findHz(String hz){
try {
List<Hanzi> lstHanzi = objHanziRepository.findByHanziContains(hz);
if ((lstHanzi.size() == 0) || (lstHanzi == null)) {
return null;
}
return lstHanzi.get(0);
}catch (Exception exp){
return null;
}
}
public List<Ciyu> findCy(String hz){
try {
List<Ciyu> lstCiyu = objCiyuRepository.findByCiyuContains(hz);
if ((lstCiyu.size() == 0) || (lstCiyu == null)) {
return null;
}
return lstCiyu;
}catch (Exception exp){
return null;
}
}
public HanziCiyu findHzcy(String hz) {
try {
List<Hanzi> lstHanzi = objHanziRepository.findByHanziContains(hz);
if ((lstHanzi.size() == 0) || (lstHanzi == null)) {
return null;
}
List<Ciyu> lstCiyu = objCiyuRepository.findByCiyuContains(hz);
HanziCiyu res = HanziCiyu.builder()
.hz(lstHanzi.get(0))
.lstCiyu(lstCiyu)
.build();
return res;
}catch (Exception exp){
return null;
}
}
//*/
}
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
?
如此这个图就成立了
?
理想的结构
|