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工具

展开阅读全文
发表评论

相关文章