Java 使用 Graphql 搭建查询服务详解

背景

随着React的开源,facebook相继开源了很多相关的项目,这些项目在他们内部已经使用了多年,其中引起我注意的就是本次讨论的是graphql,目前官方只有nodejs版,由于很多公司的后台技术栈都是Java,所以便有了graphql的java版实现,在github上可以找到,废话不多说,直接看代码吧,具体介绍还是去看官网吧,不然就跑题了。

GraphQLSchema

Schema相当于一个数据库,它有很多GraphQLFieldDefinition组成,Field相当于数据库表/视图,每个表/视图又由名称、查询参数、数据结构、数据组成.

1) 先定义一个数据结构(GraphQLOutputType)字段,然后定义一个初始化方法

private GraphQLOutputType userType;

private void initOutputType() {
   /**
    * 会员对象结构
    */
   userType = newObject()
       .name("User")
       .field(newFieldDefinition().name("id").type(GraphQLInt).build())
       .field(newFieldDefinition().name("age").type(GraphQLInt).build())
       .field(newFieldDefinition().name("sex").type(GraphQLInt).build())
       .field(newFieldDefinition().name("name").type(GraphQLString).build())
       .field(newFieldDefinition().name("pic").type(GraphQLString).build())
       .build();
}

2)再定义两个表/视图,它包括名称,查询参数,数据结构,以及数据检索器

 /**
   * 查询单个用户信息
   * @return
   */
  private GraphQLFieldDefinition createUserField() {
    return GraphQLFieldDefinition.newFieldDefinition()
        .name("user")
        .argument(newArgument().name("id").type(GraphQLInt).build())
        .type(userType)
        .dataFetcher(environment -> {
          // 获取查询参数
          int id = environment.getArgument("id");

          // 执行查询, 这里随便用一些测试数据来说明问题
          User user = new User();
          user.setId(id);
          user.setAge(id + 15);
          user.setSex(id % 2);
          user.setName("Name_" + id);
          user.setPic("pic_" + id + ".jpg");
          return user;
        })
        .build();
  }

  /**
   * 查询多个会员信息
   * @return
   */
  private GraphQLFieldDefinition createUsersField() {
    return GraphQLFieldDefinition.newFieldDefinition()
        .name("users")
        .argument(newArgument().name("page").type(GraphQLInt).build())
        .argument(newArgument().name("size").type(GraphQLInt).build())
        .argument(newArgument().name("name").type(GraphQLString).build())
        .type(new GraphQLList(userType))
        .dataFetcher(environment -> {
          // 获取查询参数
          int page = environment.getArgument("page");
          int size = environment.getArgument("size");
          String name = environment.getArgument("name");

          // 执行查询, 这里随便用一些测试数据来说明问题
          List<User> list = new ArrayList<>(size);
          for (int i = 0; i < size; i++) {
            User user = new User();
            user.setId(i);
            user.setAge(i + 15);
            user.setSex(i % 2);
            user.setName(name + "_" + page + "_" + i);
            user.setPic("pic_" + i + ".jpg");
            list.add(user);
          }
          return list;
        })
        .build();
  }

3)接着定义一个Schema,并将其初始化,它包含一个名称,以及一个或多个表/视图(Field)

 private GraphQLSchema schema;

  public GraphSchema() {
    initOutputType();
    schema = GraphQLSchema.newSchema().query(newObject()
        .name("GraphQuery")
        .field(createUsersField())
        .field(createUserField())
        .build()).build();
  }

4)完成以上步骤之后,还需要定义一个model,类名不限,但是结构需要满足前面定义的数据结构,而且必须是public的

public class User {
  private int id;
  private int age;
  private int sex;
  private String name;
  private String pic;
  // getter, setter...
}

5)之后写一个main方法,来测试一下

public static void main(String[] args) {
    GraphQLSchema schema = new GraphSchema().getSchema();

    String query1 = "{users(page:2,size:5,name:\"john\") {id,sex,name,pic}}";
    String query2 = "{user(id:6) {id,sex,name,pic}}";
    String query3 = "{user(id:6) {id,sex,name,pic},users(page:2,size:5,name:\"john\") {id,sex,name,pic}}";

    Map<String, Object> result1 = (Map<String, Object>) new GraphQL(schema).execute(query1).getData();
    Map<String, Object> result2 = (Map<String, Object>) new GraphQL(schema).execute(query2).getData();
    Map<String, Object> result3 = (Map<String, Object>) new GraphQL(schema).execute(query3).getData();

    // 查询用户列表
    System.out.println(result1);
    // 查询单个用户
    System.out.println(result2);
    // 单个用户、跟用户列表一起查
    System.out.println(result3);

}

输出:

{users=[{id=0, sex=0, name=john_2_0, pic=pic_0.jpg}, {id=1, sex=1, name=john_2_1, pic=pic_1.jpg}, {id=2, sex=0, name=john_2_2, pic=pic_2.jpg}, {id=3, sex=1, name=john_2_3, pic=pic_3.jpg}, {id=4, sex=0, name=john_2_4, pic=pic_4.jpg}]}
{user={id=6, sex=0, name=Name_6, pic=pic_6.jpg}}
{user={id=6, sex=0, name=Name_6, pic=pic_6.jpg}, users=[{id=0, sex=0, name=john_2_0, pic=pic_0.jpg}, {id=1, sex=1, name=john_2_1, pic=pic_1.jpg}, {id=2, sex=0, name=john_2_2, pic=pic_2.jpg}, {id=3, sex=1, name=john_2_3, pic=pic_3.jpg}, {id=4, sex=0, name=john_2_4, pic=pic_4.jpg}]}

6)最后把main方法里面的代码放到web层,只需要定义一个query参数,很容易就把查询服务搭建好了,dataFetcher 里面还是调用原来的查询接口

7)引入maven依赖

<dependency>
  <groupId>com.graphql-java</groupId>
  <artifactId>graphql-java</artifactId>
  <version>2.0.0</version>
</dependency>

关于graphql查询什么定义,看看这个或许对你有帮助

json


{
  id=6,
  sex=0,
  name="Name_6",
  pic="pic_6.jpg"
}

query

{
  id,
  sex,
  name,
  pic
}

后面那部分,其实就是json字符串,去掉=和value的结果,还是可读的

结语

graphql 带了一种全新的思维方式,可以简化web api的开发,由客户端指定需要什么数据,服务端返回什么数据,减少不必要的流量传输,对移动端友好,还提供多种数据聚合查询,多个查询只是用一个请求,既满足api最小粒度,又满足前端需要,减少请求,提高性能。

感觉以后会朝这方面去发展,大趋所驱。

(0)

相关推荐

  • javascript getElementById 使用方法及用法

    document.getElementById("link").href; document.getElementById("link").target; document.getElementById("img").src; document.getElementById("img").width; document.getElementById("img").height; document.getEl

  • JavaScript 下拉菜单实现代码

    JavaScript下拉菜单 * { padding:0; margin:0; } body { font-family:verdana, sans-serif; font-size:small; } #navigation, #navigation li ul { list-style-type:none; } #navigation { margin:20px; } #navigation li { float:left; text-align:center; position:relati

  • Java环境变量的设置方法(图文教程)

    安装JDK 向导进行相关参数设置.如图: 正在安装程序的相关功能,如图: 选择安装的路径,可以自定义,也可以默认路径.如图: 成功安装之后,进行测试是否真的成功安装,点击[开始]----[运行]----输入 CMD,在命令提示符里面输入"Java -version"并按回车键,出现下图,即为安装成功.如图: 下面开始配置环境变量,右击[我的电脑]---[属性]-----[高级]---[环境变量],如图: 选择[新建系统变量]--弹出"新建系统变量"对话框,在&quo

  • JAVA 十六进制与字符串的转换

    toHexString public static String toHexString(int i)以十六进制的无符号整数形式返回一个整数参数的字符串表示形式. 如果参数为负,那么无符号整数值为参数加上 232:否则等于该参数.将该值转换为十六进制(基数 16)的无前导 0 的 ASCII 数字字符串.如果无符号数的大小值为零,则用一个零字符 '0' ('\u0030') 表示它:否则,无符号数大小的表示形式中的第一个字符将不是零字符.用以下字符作为十六进制数字: 0123456789abcd

  • java.net.SocketException: Connection reset 解决方法

    自从SEOTcs系统11月份24日更新了一下SEO得分算法以来,一直困扰我的一个问题出现了,java的数据job任务,在执行过程中会经常报以下的错误: "2011-12-03 18:00:32 DefaultHttpClient [INFO] I/O exception (java.net.SocketException) caught when processing request: Connection reset by peer: socket write error2011-12-03

  • JAVA正则表达式 Pattern和Matcher

    1.简介: java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. 它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表现模式. Matcher 一个Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查. 首先一个Pattern实例订制了一个所用语法与PERL的类似的正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字

  • JAVA8 十大新特性详解

    "Java is still not dead-and people are starting to figure that out." 本教程将用带注释的简单代码来描述新特性,你将看不到大片吓人的文字. 一.接口的默认方法 Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下: 复制代码 代码如下: interface Formula {    double calculate(int a); default do

  • java写入文件的几种方法分享

    一,FileWritter写入文件 FileWritter, 字符流写入字符到文件.默认情况下,它会使用新的内容取代所有现有的内容,然而,当指定一个true (布尔)值作为FileWritter构造函数的第二个参数,它会保留现有的内容,并追加新内容在文件的末尾. 1. 替换所有现有的内容与新的内容. new FileWriter(file);2. 保留现有的内容和附加在该文件的末尾的新内容. 复制代码 代码如下: new FileWriter(file,true); 追加文件示例 一个文本文件,

  • javascript 手机号码正则表达式验证函数 原创

    复制代码 代码如下: function checkMobile(){     var sMobile = document.mobileform.mobile.value     if(!(/^1[3|4|5|8][0-9]\d{4,8}$/.test(sMobile))){         alert("不是完整的11位手机号或者正确的手机号前七位");         document.mobileform.mobile.focus();         return false;

  • javascript的console.log()用法小结

    console.log 原先是 Firefox 的"专利",严格说是安装了 Firebugs 之后的 Firefox 所独有的调试"绝招". 这一招,IE8 学会了,不过用起来比 Firebugs 麻烦,只有在开启调试窗口(F12)的时候,console.log 才能出结果,不然就报错. 今天看到 Opera 也有个叫 dragonfly 的东东,用这东西查看 DOM,已经可以和 Firebug 媲美,然而还是不能用 console.log.于是有人就提供了这样两句

  • Javascript 的addEventListener()及attachEvent()区别分析

    Mozilla中: addEventListener的使用方式: target.addEventListener(type, listener, useCapture); target: 文档节点.document.window 或 XMLHttpRequest. type: 字符串,事件名称,不含"on",比如"click"."mouseover"."keydown"等. listener :实现了 EventListene

  • javascript:;与javascript:void(0)使用介绍

    最近看了好几个关于<a>标签和javascript:void(0)的帖子,谨记于此,以资查阅. 注:以下代码未经全面测试,但每一种方法可能会出现的情况都基本做了说明. 在做页面时,如果想做一个链接点击后不做任何事情,或者响应点击而完成其他事情,可以设置其属性 href = "#",但是,这样会有一个问题,就是当页面有滚动条时,点击后会返回到页面顶端,用户体验不好. 目前有如下几种解决办法: 1)点击链接后不做任何事情 <a href="javascript:

随机推荐