jSqlBox 5.0.15 发布,300 行代码干掉 GraphQL

前言

看GraphQL不爽很久了,一直认为这是个鸡肋技术,过分复杂,功能有限,定位不清,存在安全问题。个人觉得GraphQL主要价值是两点,一是提供了一种模式,把业务逻辑前推到前端,让前端动态查询,第二个是结构化查询,输出结果和输入结构一样,所见即所得。前者个人认为只有用MyServerless的开发模式才能理想地同时解决安全和开发效率问题,后者则是本次更新内容,即在jSqlBox这个后端ORM工具添加类似GraphQL的结构化查询功能,但要做到不像GraphQL那么复杂,要让学习和使用成本最低。

顺便介绍一下
jSqlBox本身,这是一个全功能开源Java数据库持久层工具,只要是与数据库操作相关的功能,jSqlBox都已具备,如DDL操作、分页、分库分表、声明式事务、关联映射查询、ActiveRecord等,所有这些功能都包含在一个1M大小的jar包中,不依赖任何第三方库。jSqlBox主要特点是Java和SQL混写,把SQL写出花来了,包括这次的主从表结构化查询也是。
使用jSqlBox只要在项目中添加以下依赖:

<dependency>
   <groupId>com.github.drinkjava2</groupId>
   <artifactId>jsqlbox</artifactId>  
   <version>5.0.15.jre8</version> <!-- 或最新版 -->
</dependency> 

本次更新内容

本次5.0.15.jre8更新增加了类似GraphQL的结构化查询功能,这个功能在编程序时发现非常简单,在原有jSqlBox基础上,只需要300行代码即可实现。
jSqlBox的主从表结构化查询是依然采用jSqlBox的Java/SQL混写方式,但是这次将查询写成方法嵌套的结构,即可实现类似GraphQL的结构化查询,输入和输出的树状结构一致,所见即所得。
jSqlBox主从表结构化查询主要优点有:
1.只需要编写针对单表查询的SQL,会自动按主从关联列名生成类似“id in (?, ?...?)”的SQL片段,并将最终查询结果组装成主从表树状结构。
2.采用纯Java和原生SQL混写,功能强,学习成本低,可以同时用Java执行复杂的参数、安全检查、写数据库等业务逻辑。
3.没有直接输出为JSON,而是输出Map/List对象或Java实体对象,查询结果可以被继续修改后再发送JSON给前端。
4.可以直接利用Java的IDE格式化和语法检查功能,不需要第三方工具。格式化功能可以直观显示出树结构的嵌套层级。  
5.jSqlBox的内嵌式SQL参数、分页、分库分表、拦截器、事务等依然可以直接使用。
6.不提供安全、权限功能,无学习成本。安全、权限这些功能不属于ORM工具的职能,应该由后端的SpringSecurity/Shiro工具包或独立的Serverless/JsonAPI服务器来提供。
7.如果结合我的MyServerless开源项目,可以实现前端直接在html里书写Java、定制主从表多级查询并返回json, 将业务逻辑前移到前端。
8.性能好,用"in"的方式进行数据库表的关联查询,不存在1+N问题。
9.源码简洁(实现这个功能仅用了300行源码,见GraphQuery.java),可扩充性好。

使用示例:

        GraphQuery q1 = //
                $("addresstb as addresses", "where id>", que("a1"), " and id<", que("a5"), pagin(1, 10), //
                        $1("usertb", key("user"), ms("userId", "id"), $("userroletb as userRoleList", ms("id", "userId"), //
                                $("roletb as roleList", ms("rid", "id"), // ms方法也可以写成DB.masterSlave()
                                        $("roleprivilegetb as rolePrivilegeList", ms("id", "rid"), //
                                                $1("privilegetb as privilege", ms("pid", "id")) //                                                 
                                        )//
                                )//
                        ), //
                                $1("select * from emailtb as email", ms("id", "userId")), //
                                $("addresstb as addressList", ms("id", "userId"), "and addressName like ?", par("addr%"))//
                        )//
                );
        GraphQuery q2 = //
                $("usertb as u", "where id>", que("u2"), pagin(1, 10), entity(User.class), //映射成User实体Bean
                        $1("emailtb as emailMap", ms("id", "userId")), //$1表示是单个元素,而不是一个List
                        $("addresstb as addressList", ms("id", "userId"))//
                );
        Object result = DB.graphQuery(q1, q2); //result是查询结果
        String json = JsonUtil.toJSONFormatted(result); //输出为JSON文本

以上示例详见单元测试下的GraphQueryTest.java,输出结果如下:

{
   "addresses":[
      {
         "addressName":"address2",
         "id":"a2",
         "userId":"u2",
         "user":{
            "id":"u2",
            "userName":"user2",
            "userRoleList":[
               {
                  "id":"3i6yaxy2fusjkgisyfhypkti9",
                  "rid":"r1",
                  "userId":"u2",
                  "roleList":[
                     {
                        "id":"r1",
                        "roleName":"role1",
                        "rolePrivilegeList":[
                           {
                              "id":"b484ze4k44xemtkstehnprhxq",
                              "pid":"p1",
                              "rid":"r1",
                              "privilege":{
                                 "id":"p1",
                                 "privilegeName":"privilege1"
                              }
                           }
                        ]
                     }
                  ]
               },
               {
                  "id":"e41dln9m4jehmc7somvu5s2pf",
                  "rid":"r2",
                  "userId":"u2",
                  "roleList":[
                     {
                        "id":"r2",
                        "roleName":"role2",
                        "rolePrivilegeList":[
                           {
                              "id":"dhrh5kgsod6w76e6xtl36u8b9",
                              "pid":"p1",
                              "rid":"r2",
                              "privilege":{
                                 "id":"p1",
                                 "privilegeName":"privilege1"
                              }
                           },
                           {
                              "id":"b9h2aenn6jjacns9ng5vwhaiq",
                              "pid":"p3",
                              "rid":"r2",
                              "privilege":{
                                 "id":"p3",
                                 "privilegeName":"privilege3"
                              }
                           }
                        ]
                     }
                  ]
               },
               {
                  "id":"994a5o65pfa7wx8vq99gi1lkg",
                  "rid":"r3",
                  "userId":"u2",
                  "roleList":[
                     {
                        "id":"r3",
                        "roleName":"role3",
                        "rolePrivilegeList":[
                           {
                              "id":"7qf9us50mw95hijwkfvuzus4q",
                              "pid":"p3",
                              "rid":"r3",
                              "privilege":{
                                 "id":"p3",
                                 "privilegeName":"privilege3"
                              }
                           }
                        ]
                     }
                  ]
               }
            ],
            "email":{
               "emailName":"email3",
               "id":"e3",
               "userId":"u2"
            },
            "addressList":[
               {
                  "addressName":"address2",
                  "id":"a2",
                  "userId":"u2"
               }
            ]
         }
      },
      {
         "addressName":"address4",
         "id":"a4",
         "userId":"u4",
         "user":{
            "id":"u4",
            "userName":"user4",
            "userRoleList":[
               {
                  "id":"bb2d1kuwvii0gpa0pxgaph8zr",
                  "rid":"r1",
                  "userId":"u4",
                  "roleList":[
                     {
                        "id":"r1",
                        "roleName":"role1",
                        "rolePrivilegeList":[
                           {
                              "id":"b484ze4k44xemtkstehnprhxq",
                              "pid":"p1",
                              "rid":"r1",
                              "privilege":{
                                 "id":"p1",
                                 "privilegeName":"privilege1"
                              }
                           }
                        ]
                     }
                  ]
               }
            ],
            "addressList":[
               {
                  "addressName":"address4",
                  "id":"a4",
                  "userId":"u4"
               }
            ]
         }
      }
   ],
   "u":[
      {
         "id":"u3",
         "userName":"user3",
         "addressList":[
            {
               "addressName":"address3",
               "id":"a3",
               "userId":"u3"
            }
         ],
         "emailMap":{
            "emailName":"email5",
            "id":"e5",
            "userId":"u3"
         }
      },
      {
         "id":"u5",
         "userName":"user5",
         "addressList":[
            {
               "addressName":"address5",
               "id":"a5",
               "userId":"u5"
            }
         ]
      }
   ]
}

用法详解(下面就是全部文档了, 一共10条,看完就学会了,看看比GraphQL简单多少!)

  • 每个数据库表格对应一个SQL查询,写在$()或$1()方法中
  • $()方法的第一个参数如果没有空格,则系统自动转换为 select * from xxx
  • $()方法的第一个参数如果有空格,如"select id, name from tb",则系统不转换
  • $()方法的第一个参数的最后一个单词,将作为输出结果的键名。键名也可以用key("键名")来手工指定。
  • ms()方法也可以写成DB.masterSlave(),它的参数是主表和从表的键名,参数个数必须是2的倍数, ms()支持复合主键,如ms("m1", "m2", "c1","c2" )表示主表的(m1,m2)列关联到从行的(c1,c2)列, 主表还是从表的判定与数据库定义无关,而是:如果一个$()方法写在另一个$()方法里,则它就是从表, ms()方法会被编译成 " where xxId in (?, ?,...,?) " 片段。问号是根据主表的所有关联列值填充为SQL参数
  • $1()方法表示仅输出单个元素而不是一个列表,$1()也可以写成$("xxxxx", DB.one)
  • 从第二个参数起,即可使用jSqlBox的内嵌sql式语法,普通文本解析为SQL片段,pagin、par、que等方法都可以使用
  • 缺省情况下,输出结果为Map/List结构,但是如果出现DB.entity(XxxClass)参数后,这个SQL的输出结果被转换为一 个实体Bean对象。实体Bean也可以嵌套从表的内容,但是要注意Bean里要有相应的字段定义。
  • 使用DB.graphQuery($(), $()...)可以对一个或多个$()方法进行查询。
  • 输出对象需要输出为Json时,需要使用者自行在pom中添加JSON工具依赖,并手工进行转换,jSqlBox是个ORM工具,本身并不提供JSON工具
优秀的个人博客,低调大师

微信关注我们

原文链接:https://www.oschina.net/news/209912/jsqlbox-5-0-15-released

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
优质分享Android(本站安卓app)

优质分享Android(本站安卓app)

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario,低调大师唯一一个Java游戏作品

Mario,低调大师唯一一个Java游戏作品

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。