V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Braisdom
V2EX  ›  Java

历经 3 年,终于得以实现了

  Braisdom · 8 天前 · 18629 次点击

写了 15 年的程序,人到中年总有很多话要说,千言万语尽在项目里,里面有我想说的一切。

项目地址: https://github.com/braisdom/ObjectiveSql

ObjectiveSQL 是我构想了很长时间,到底是让 Java 像 SQL 一样编程,还是让 SQL 让 Java 一样编程,纠结了很久,还是让 Java 更像 SQL,Java 的语法表现力不够,只能扩展 Javac,实现了算法运算,比较运算,逻辑运算符重载,并封装了常用数据的的函数,抽象了 Expression,使的 Java 非常接的 SQL,同时也实现了简单 SQL 编程的代码生成,基本不需要写代码,也不需要配置就能实现简单 SQL 的编程,话不多说,先看示例,有兴趣可以到 github 上看。

Order.Table orderTable = Order.asTable();
Select select = new Select();

select.project(sum(orderTable.amount) / sum(orderTable.quantity) * 100)
    .from(orderTable)
    .groupBy(orderTable.productId);
Member.Table member = Member.asTable();
Order.Table order = Order.asTable();

Select select = new Select();

select.from(order, member)
        .where(order.memberId.eq(member.id));
select.project(member.no,
        member.name,
        member.mobile,
        countDistinct(order.no).as("order_count"),
        sum(order.quantity).as("total_quantity"),
        sum(order.amount).as("total_amount"),
        min(order.salesAt).as("first_shopping"),
        max(order.salesAt).as("last_shopping"));
select.groupBy(member.no, member.name, member.mobile);

ObjectiveSQL is an ORM framework in Java base on ActiveRecord pattern, which encourages rapid development and clean, codes with the least, and convention over configuration.

Features

  • Dynamic code generation with JSR 269 for Java API of database access
  • Full Java API of database access without coding
  • Dynamically SQL programming with Java syntax, and very close to SQL syntax
159 条回复    2020-11-25 15:11:45 +08:00
1  2  
Braisdom
    101
Braisdom   7 天前
@fangcan 感谢支持
pythonee
    102
pythonee   7 天前   ❤️ 1
点赞
有对比过 linq 吗
bugmakerxs
    103
bugmakerxs   7 天前   ❤️ 1
其实解决 orm 还有另一个思路是避免复杂 sql,比如微服务;如果一定要复杂 sql 才能解决的问题,我们一般都交给大数据团队做离线计算了。个人认为复杂 sql 不管怎么表达,,都挺难维护的。。
Braisdom
    104
Braisdom   7 天前
@bugmakerxs 离线计算需要一定的前提,离线计算只是输出中间结果,前台的计算复杂度依然存在。
Braisdom
    105
Braisdom   7 天前
@pythonee 你有兴趣可以看一下贴子前面的讨论,Linq 分为两种,1 )内存计算,2 ).NET Entity Framework 实现,很优秀,值得借鉴
liuhan907
    106
liuhan907   7 天前 via Android
@Braisdom 其实 ef core 最优秀,或者说最能节省精力的地方在于自动追踪查询出来的结构的数据变化并生成 update 语句。所有的操作都是强类型。不过性能上就弱一些。再次看了你的项目的 repo,你这个我觉得更加类似 dapper 。
beginor
    107
beginor   7 天前 via Android
不知楼主有没有兴趣了解一下这个 https://github.com/re-motion/Relinq, .Net 上的不管是 EF 还是 NHibernate, 都是用了这个来进行 linq 表达式树的转换
beginor
    108
beginor   7 天前 via Android
@beginor 手滑了, 正确的地址是 https://github.com/re-motion/Relinq
momocraft
    109
momocraft   7 天前
有点像 slick
momocraft
    110
momocraft   7 天前
纯用 java 做这个真的有毅力..
miaoever
    111
miaoever   7 天前
我司 (也算大厂了吧) 做过类似的事情, 主要针对的场景是 OLAP 的 SQL. 我们的做法是提供两套 API ,一套是 SQL 本身,另一套是类似于楼主的这种链式调用 API (不过我们是 Python), 同时我们两套 API 可以混用 .

我们做这个工作一个重要的出发点能够对 SQL 进行静态的类型检查 (以及其他的一些语义检查). 做法上和楼主不一样的是, 我们不基于宿主语言 (Java or Python) 的类型系统,而是将 SQL 或者 链式 API 的程序统一翻译成一个中间表达形式 (IR), 然后再其上做类型检查. 此外, 我们不仅仅是对单个 SQL 做类型检查, 更重要的是对整个 OLAP 链路上所有具有数据血缘的 SQL 会做全局检查, 以保证链路上任何 SQL 的修改都都不会破坏其上游及下游 SQL 的合法性.
当然类型检查只是我们做的一方面, 我们还做了很多其他的更重要的语义检查, 在这儿就先不展开说了.


另一很重要的动机是 SQL 的的提高可组合型以及复用性. 举个简单的例子, 用楼主的给的场景来说, 对于以下的这个计算(当然我们的现实中的场景比这个复杂的多):

sum(orderTable.amount) / sum(orderTable.quantity) * 100

我们的系统里, 这个计算可以定义成一个库函数 (def sum(double)->double). 这个函数可以像普通编程语言中的库函数一样在任何一个 SQL 中进行调用:

SELECT sum(1.0) FROM table1
// SELECT.PROJECT(sum(1.0)).....

同时, 我们的静态检查工具能够静态的当前调用环境中 SUM 函数时输入类型和返回类型做类型检查.
Mithril
    112
Mithril   6 天前
支持,Java 和.NET 都写的表示 Java 真的缺少 Linq 这种东西。
Linq to object 自动在内存中处理所有 IEnumerable,而且支持并行。
Line to SQL 会自动编译成对应的 SQL,而且无法编译的现在并不会自动在内存处理,会直接报错。虽然免不了手写 SQL,但不是整个项目所有地方都需要真正手工优化 SQL 的。
最关键的是两者语法基本一致,写起来非常流畅。
wanguorui123
    113
wanguorui123   6 天前 via iPhone
有点像 FreeSQL
lqw3030
    114
lqw3030   6 天前 via iPhone
不管怎么样先嘲讽,思想就很有问题
luoyikang52066
    115
luoyikang52066   6 天前 via Android
支持
CallMeADU
    116
CallMeADU   6 天前
出发点不错,技术能力也很强。不过从多数程序员角度肯定是不喜欢用 java 代码去实现 sql 生成的,一不直观,二代码有 sql 写的爽吗?还是喜欢各自领域各司其职
Braisdom
    117
Braisdom   6 天前
@lqw3030 我的想法是很有问题的,充满了对现实的不满,我之前写了四,五年的 Ruby, Python,回到 Java 尽然快 20 年了一直都没什么变化,十分气愤
Braisdom
    118
Braisdom   6 天前
@miaoever 通过函数实现算术表达现在比较流行,但表达式过于复杂,函数也力不从心的。

例如:
IF((COUNT(DISTINCT IF((dateDiff('second', t1.finish_time, t2.sales_at) AS t) < 14*24*60*60 AND t > 0, t2.member_no, NULL)) AS p6) IS NULL, 0, p6) AS sales_member_count

这样的表达式,如果全部是函数封装,根本没人看得懂
wysnylc
    119
wysnylc   6 天前
支持聚合函数的 JPA,但是分页子查询排序这些问题不可能维护的完的
Braisdom
    120
Braisdom   6 天前
@wysnylc 复杂性是无法避免的问题,也是客观的事实,ObjectiveSQL 的目的是让复杂性变得更清晰,更可读,而不能完全降低复杂性。
ZiLong
    121
ZiLong   6 天前   ❤️ 2
点赞,支持,这是有人不断提出挑战,落实行动,才会有不断的进步
zclHIT
    122
zclHIT   6 天前   ❤️ 1
aiden4
    123
aiden4   6 天前
rails 就是这样子写 sql 的,用起来还是很舒服的。java 确实缺少这样的东西
mazhimazh
    124
mazhimazh   6 天前
支持,哈哈,你历时 3 年写软件,我历时 3 年是研究 JVM 源代码,并且写 3 本书,明年 8 月就 3 年,也快完成了
GM
    125
GM   6 天前
已经很不错了,点个赞。

其实我一直在找类似 ActiveRecord 的 Java 实现,一直没找到感觉好用的,ActiveJDBC 、ActiveJpa 都不够好用。

你这参考 lombok 的思路倒是个方法,可以用来实现一套 ActiveRecord for Java 。
Braisdom
    126
Braisdom   6 天前
@GM 是的,那些我都尝试过做的都不是特别理想,不实用,太过 Java 了
zoharSoul
    127
zoharSoul   6 天前
感觉用 kotlin 实现好一些.
天然支持中缀表达式和运算符重载.

熟悉起来会更像 sql
cmdOptionKana
    128
cmdOptionKana   6 天前
@zoharSoul 对哦,这个改成 kotlin 看起来会很酷,甚至可能比 Linq 更酷!
Braisdom
    129
Braisdom   6 天前
@zoharSoul 非常认可你的想法,这也是 Java 语言糟糕的地方,不够灵活
但话说回来,Kotlin 语言想成为主流,过程还是很漫长的,Android 后续也会向 Flutter 倾斜,Kotlin 后面的发展比较难呀。
很多好东西不一定会流行,但历史会记住它的
Braisdom
    130
Braisdom   6 天前
如果 ObjectiveSQL 后续的发展能够起来,我准备在 Python 端探索方向,Python 的受众群体也是比较大的。
122006
    131
122006   6 天前
用类似于 lombok 实现运算符重载,太强了。
安卓不知道能不能支持,理论上 lombok 也可以说明应该只是兼容的问题,在安卓中直接写 sql 比较少的,如果可以证明稳定性的话推广起来比 springboot 上容易太多
vitoliu
    132
vitoliu   6 天前   ❤️ 1
支持,感受产品->创造产品->激发新产品
zoharSoul
    133
zoharSoul   6 天前
@Braisdom
#129
目前 android 上 kotlin 已经是主流的, 官方已经 all in kotlin 了.
在传统的后端领域还不好说.
cmdOptionKana
    134
cmdOptionKana   6 天前
@Braisdom 楼主,请看一下 kotlin 的这个特性,就花你 2 分钟时间 https://medium.com/makingtuenti/infix-functions-in-kotlin-2db3d3142dd2

用上这个,可以让你这个项目看起来很 geek, 很酷!
zsc8917zsc
    135
zsc8917zsc   6 天前   ❤️ 1
Java 版的 Linq2SQL,点赞支持,向大佬致敬
Braisdom
    136
Braisdom   6 天前
@cmdOptionKana Kotlin 的语法的确比较优美
语法表现力更强,相比 Java, C 这类语言太过传统,长远来看它们还是什么被淘汰的。
就像喝白酒一下,现在的年轻人,喝白酒的越来越少了。
brezp
    137
brezp   6 天前
支持, 下次自己的项目拿来试试
Braisdom
    138
Braisdom   6 天前
@brezp 感谢支持,我现在正在开发分页的特性,相比其它 ORM 框架,分页使用起来也会特别简单
bk201
    139
bk201   6 天前
我是不明白为啥要把可以让 dba 去优化的 sql 写成代码。
Braisdom
    140
Braisdom   6 天前
@bk201 你可以看一下前面我的解释,
DBA 和程序员 需要的是不同的 SQL 编程方式,程序员需要的更多是动态的 SQL 构造过程,传统 SQL 远远做不到。
CatTom
    141
CatTom   6 天前
老哥,我在导入你的 jar 时 maven 一直报错识别不到。。。。。
Braisdom
    142
Braisdom   6 天前
@CatTom 你加我微信, braisdom
Braisdom
    143
Braisdom   6 天前
@CatTom 稍等,我知道原因了。
bxb100
    144
bxb100   6 天前
有性能优化的比较吗
Braisdom
    145
Braisdom   6 天前
@CatTom 这两天一直写分页的,忘记发布一个 pom 了,不好意思,正在搞。
Braisdom
    146
Braisdom   6 天前
@bxb100 你所说的性能是指哪方面性能
bxb100
    147
bxb100   6 天前
@Braisdom #146 内存, 速度之类的
Gugetech
    148
Gugetech   6 天前   ❤️ 1
很赞哦,已 star
Braisdom
    149
Braisdom   6 天前
@bxb100 老实说,性能指标还真的不好搞,主要因为 ObjectiveSQL 是一个无状态的框架,本身不存储任何东西,唯一需要验证的是内存泄露,SQL 的构造也都是字符串拼接,本质上不会有性能问题。
a4854857
    150
a4854857   6 天前   ❤️ 1
等楼主成了.以后有人再来挖贴的时候
记得我是第一个叫你 B 大的人
henryshen233
    151
henryshen233   6 天前
@Braisdom 楼主,#114 好像说的是首页有些人说话语气有问题
Braisdom
    152
Braisdom   6 天前
@CatTom 抱歉,1.3.8 版本是我测试不充分,我调整了 pom 依赖,
tt0411
    153
tt0411   6 天前   ❤️ 1
为楼主点个赞.

但从实用角度和个人偏好来说, 能直写 sql 就绝不用各类 dsl/自动 SQL 生成 的框架: 一方面专门学一套新的接口 ROI 不高, 另一方面真的遇到问题是, 看 SQL 改 SQL 也比接口更容易.
Braisdom
    154
Braisdom   6 天前
@tt0411 其实我不是很喜欢 DSL 这个概念,按道理说 SQL 是一个 DSL,而 Java 是基于自身的抽象能力与表达能力使得 SQL 更便于逻辑控制。
当然,这是我个人的感觉,每个人有不同的背景,也会有不一样的认知,没有对与错,只是会用不同的方式进行工作而已。
Braisdom
    155
Braisdom   5 天前
@CatTom 已经更新,可以按 readme 更新一下版本信息
aguesuka
    156
aguesuka   5 天前 via Android
楼主了解依赖类型(dependent type)吗。我想过为什么 java 表现不了 sql,这个是答案之一。不过 sql 是基于集合论而非类型论,所以拘束于 sql 语法是实现不了类型安全的
Braisdom
    157
Braisdom   5 天前
@aguesuka 你说的很对,SQL 语言是一种已经被习惯的数据查询和分析语言,最初的设计是直接给人使用的,而不是为程序设计的,逻辑控制、强类型等程序化的概念的使用是需要专业训练的。

我挑战的是 SQL 语言在程序上的表现力,目前只是第一个版本,我的想法还没有充分彰显。

对权威的挑战必定会迎来更多人的挑战,这是正常的,没有对权威的挑战,这个时代也就不会进步了。
Joker123456789
    158
Joker123456789   4 天前
我也是做开源项目的,也遭遇过不少质疑,从这个角度来看,我真的很想说一些鼓励的话,甚至支持一下。我最近也看到这个项目在 osc 上得奖了。

但是我还是想理性分析一下:

1. 一般来说,我们在开发的时候如果需要操作数据库,会怎么办?我想大多数人都是在 navicat 里写 sql,测好以后,再贴到项目中来, 而用了你这个,只需要直接在项目中写代码即可,省去了粘贴的步骤。

2. 第 1 点看似是提高了效率,但是也引发了另一个问题,如果在 navicat 里写 sql,用工具也可以通过可视化的方式生成 sql,写起来并不慢,而且验证速度非常快,只需要点击一个按钮即可, 而在项目中用你这个写的话,验证就得启单测了,这个速度相差的可不是一点点。

我之前也想过要在我的项目中加入类似的功能,但是考虑到开发效率,就放弃了。毕竟一切的工具都是为了提高干活的效率,或者运行效率,如果没有这个前提的话,所有的花样都只是一种新鲜感。

如果你可以解决这个问题的话,比如开发配套的 idea 插件 或者 其他方法,相信大家会更愿意使用的。
Braisdom
    159
Braisdom   4 天前
@Joker123456789 你的分析很到位,一个工具很难做到面面俱到,不太可能适应所有的场景,有舍才有得。
1 ) ObjectiveSQL 主要解决动态 SQL 的编程,所谓动态:也就是 SQL 结构很难通过字符串接近的形式实现,这种场景下,传统 SQL 想变为程序很困难,将这样的程序直接变成 SQL 也不容易,ObjectiveSQL 会通过单元测试的形式提供完成 SQL 。

2 )其实,程序员到底是看到 SQL 呢,还是还到 Java 一直是矛盾的中点,试想一下,如果没有 SQL 的存在,大家直看 Java 会是什么样的一种状态。
1  2  
关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1021 人在线   最高记录 5268   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 24ms · UTC 19:48 · PVG 03:48 · LAX 11:48 · JFK 14:48
♥ Do have faith in what you're doing.