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

相關推薦

2023-09-22

支持Swagger. Hibernate/MyBatis+ plus +Sharding JDBC + Jpa+ Spring data+ GraphQL+ App ORM (Android, 鸿蒙)= Bee 要整合一堆的工具,还不如只用一个小巧又功能强大的工具。犹如 JAVA 界的数据源连接池 Hikari, 文件虽小,功能却不赖!   V2.1.8 (2

2024-01-06

探索视频(用户录制): 最近更新了什么? 新增 graphql-solon-plugin 插件(欢迎试用) 修复 @Header 与 @Body 同时注入时,@Header 会失效的问题 修复 LocalCacheService 时间过大时会超界的问题 添加 local-solon-cloud-plugin 对描述信

2023-07-04

DE 内的 Redoc 和 Swagger UI 之间切换。 HTTP Client CLI 中对 GraphQL 和 WebSocket 的支持 现在可以使用 IntelliJ IDEA 中的 HTTPClient CLI 与 GraphQL API 交互,并与服务建立 WebSocket 连接,例如用于测试或自动化脚本。 检查 YAML 文件

2022-10-24

Dgraph 是一个水平可扩展的分布式 GraphQL 数据库,有一个图形后端。作为一个原生的 GraphQL 数据库,它严格控制数据在磁盘上的排列方式,以优化查询性能和吞吐量,减少集群中的磁盘寻道和网络调用。 Dgraph v22.0.0 发布后将停

2022-10-18

schedule 任务取消的操作逻辑 升级 snakeyaml 为 1.32 升级 graphql 为 18.3 升级 snack3 为 3.2.44 升级 water 为 2.9.0 进一步了解 Solon: 《想法与架构笔记》 《生态预览》 《与 Spring Boot 的区别?》 《与 Spring Cloud 的区别?》

2024-08-06

Dgraph 是一个水平可扩展的分布式 GraphQL 数据库,有一个图形后端。作为一个原生的 GraphQL 数据库,它严格控制数据在磁盘上的排列方式,以优化查询性能和吞吐量,减少集群中的磁盘寻道和网络调用。 Dgraph v24.0.1 现已发布,具

2024-08-19

Dgraph 是一个水平可扩展的分布式 GraphQL 数据库,有一个图形后端。作为一个原生的 GraphQL 数据库,它严格控制数据在磁盘上的排列方式,以优化查询性能和吞吐量,减少集群中的磁盘寻道和网络调用。 Dgraph v24.0.2 现已发布,具

2023-09-22

都能四世同堂,凭什么开发 30 岁就要被干掉? Go 团队发布了一份官方指南,帮助开发者更规范地组织/构建 Go module。 刚接触 Go 的开发者常见问题之一是,“就文件和文件夹的组织布局而言,如何组织我的 Go 项目?”。这

2023-07-11

Dgraph 是一个水平可扩展的分布式 GraphQL 数据库,有一个图形后端。作为一个原生的 GraphQL 数据库,它严格控制数据在磁盘上的排列方式,以优化查询性能和吞吐量,减少集群中的磁盘寻道和网络调用。 Dgraph v23.0.1 现已发布,具

2023-09-22

” 启动3-5秒,启动内存节省1/3(有兴趣的,欢迎拉取代码体验) 有什么可爱的特点? 启动快 5 ~ 10 倍。 (更快) qps 高 2~ 3 倍。 (更高) 运行时内存节省 1/3 ~ 1/2。 (更少) 打包可以缩小到 1/2 ~ 1/1

2022-11-15

们的批处理工作。”最后还质问马斯克是否有去了解过 GraphQL 的工作原理。 GraphQL 是一个用于 API 的查询语言,也是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数

2022-11-11

平台,只需要一个简单的 JPA 注释模型就能帮你轻松搭建 GraphQL 和 JSON API web 服务。具有标准完善的数据安全保障、移动端性能优化 API、任何数据写入都可以保证原子性(Atomicity)、支持自定义数据持久化机制、数据模型一览无

2022-10-17

数据里,为用户提供足够低延迟的实时查询。Dgraph 支持 GraphQL 作为查询语言,响应 JSON。 Dgraph v22.0.0-RC1 现已发布,v22.0.0 是基于 v21.03.2 所发布的。官方警告称,他们将停止对 v21.12.0 的支持。 本次具体更新内容如下: Fixed

2023-09-22

数据技术都能四世同堂,凭什么开发 30 岁就要被干掉? PrimiHub 是一款由密码学专家团队打造的开源隐私计算平台,支持安全多方计算、联邦学习、隐私求交、隐私查询等。具有如下特性: 开源:完全开源、免费 安装简