基于JPA体验GraphQL的组合查询能力

嗯,前端会一些后台的开发很是重要......

诺,后端如果会一点前端,也同样重要......

 

GraphQL

【后端同学】我们通常会写很多API,特别是业务成长期,产品希望保障良好的体验的时候,他们会要求后端组合一堆API来满足页面的刚刚好的要素需求。特别是如下图橙色部分的组合API,真的是一个噩梦。前端同学与后端同学一直会不停地纠结。

 

看我72般变化


【前端同学】我们通常会调用很多API,哪怕只是增加一个要素,我也要多调用一次接口。没办法页面要,后端有(无语的是他们分了好多个接口,有时候鉴权还不一样)。你说就这样,页面的体验,效率能怨我么?

 

前端开发的烦恼

于是架构师来了,我们做个组合,做个读写模型分析,抽取共性,做到需要的数据就触发查询,再对数据进行编排组合来满足前端的诉求。把大家拉来评审下,避免API风暴......

但,等等,是否应该做一个通用查询,如这样?

 

统一组合查询API

按需触发接口,组合数据供页面使用,这就是GraphQL面对的问题域。我识别GraphQL的特点如下:

  1. 立足于从产品经理角度看问题,某种意义上是为前端开发解决问题
  2. 提供了强大的语法来解释嵌套数据结果,基于强类型,提供了相应的一些质量保障
  3. 语言支持丰富,如下图

 

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

 

如此这个图就成立了

 

理想的结构

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值