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

分析一下 Java ORM 框架的原理,大家怎么看?

  •  1
     
  •   Braisdom ·
    braisdom · 2020-12-15 12:45:59 +08:00 · 4784 次点击
    这是一个创建于 1437 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Java 的 ORM 框架发展了很多年,一直存在各种各样的问题,相信大家的体会比我还要深,到底这是什么什么呢?我从两个角度来分析一下,大家看看对不对?

    1 )语义表达的双重性

    一个 Java Bean 的定义,只能表达一种语义,就是与其它相关的 Java 类相互协作实现一项特性,通常用于数据的载体,但不能表达数据库交互时的 SQL 的语义,两种内存的语义差异很大(例如:Java Field 在数据库中映射为 Column,一个 Column 可以进行排序、算术运算、比较运算等),很难通过 Annotation 进行描述,除非 Java 中增加新的特性,用于描述一个 Java Class 的多重语义。既然一个 Java Class 无法满足双重语义的描述,只能新增代码进行定义和描述,此时又出现重复代码和变更的一致性,通过 Maven 或 APT 形式生成,也会存在每次变更后,都需要重新运行任务,才能正常使用,使的开发过程变得更糟。

    2 )语法一致性

    Java 语法与 SQL 语法从语义上说基本是一致的,但表现形式不一样,例如:Java 中逻辑表达式:a && b 或者 a || b,而 SQL 中则是 a AND b 或者 a OR c,两种类型表达语义上一致,但语法上不能直接互换。同时,也无法 Java 对语义的解析和执行过程进行干预,也就导致了 Java 的表达式无法直接转换为 SQL 表达式,只能以字符串的形式进行拼接,通过对字符串拼接过程进行抽象和封装,形成一系列 API,这也就导致了简单的表达式变成若干 Java 方法的嵌套,严重影响了代码的可理解性。

    ObjectiveSQL 完美的解决了上述两个问题,通过 Java 代码的动态生成和 Java 运算符重载等特性,有兴趣的同学可以发起挑战: https://github.com/braisdom/ObjectiveSql

    34 条回复    2020-12-16 17:57:33 +08:00
    wangyanrui
        1
    wangyanrui  
       2020-12-15 12:57:37 +08:00   ❤️ 1
    支持一下楼主,简单看了一些 Readme,ORM 的程度弱于 JPA 之类,但是远强于 MyBatis,如果是个人开发的话,估计是熬了不少个通宵😂😂😂😂

    另:JAVA 是面向对象的,RDBMS 不是面向对象的,这玩意就是个死结呀~
    Braisdom
        2
    Braisdom  
    OP
       2020-12-15 13:06:18 +08:00
    @wangyanrui 感谢兄弟,面向对象和数据库本质上是不冲突的,数据库的特性以对象关系型计算为基础,仅仅是一个数据存储和计算引擎,与语言的特性关系不是太大。
    neochen13
        3
    neochen13  
       2020-12-15 13:09:24 +08:00   ❤️ 12
    老哥,我现在看到你就知道你想推广自己的东西

    每篇都这样,越看越反感
    wangyanrui
        4
    wangyanrui  
       2020-12-15 13:09:37 +08:00 via Android
    可能最后一句没描述清楚吧,我的意思是什么时候数据库支持面相对象了,就破费特了😂
    Braisdom
        5
    Braisdom  
    OP
       2020-12-15 13:13:04 +08:00
    @neochen13 兄弟对不住了,我也只是想写一点干货,也是我的理解,希望对你有价值,你可以忽略后面的链接


    @wangyanrui 对象化的数据库很多年前就已经有人在尝试了,但也难以超越关系计算的理论,总是绕不开呀。
    hantsy
        6
    hantsy  
       2020-12-15 13:23:25 +08:00
    好像有一个产品叫 ObjectDB,原生支持 JPA API 。
    hantsy
        7
    hantsy  
       2020-12-15 13:27:08 +08:00
    @wangyanrui OR Mapping Mismatching 是个老话题了。JPA 主 OOP,不得不让数据库做一些妥协,比如 JPA 继承的设计,对应的表的结构不符合数据库的范式。
    Braisdom
        8
    Braisdom  
    OP
       2020-12-15 13:35:08 +08:00
    @hantsy 恩,不同类型的数据存储方式,和不同类型外部访问和计算方式,一直在挑战传统关系型数据,早些年 Google 为主发起的 NoSQL 系列数据库风头很盛。
    我个人觉得,分久必合,合久必分,关系数据库统治数据库领域好多年了,在不同行业,不同数据和计算特征的领域会有新型的数据库出现,但能不能超越由关系数学这种高度抽象的概念衍生的关系计算模型就难说了。

    我公司的项目里用了 Apache Calcite 作为融合数据的计算引擎,解决不同存储格式的数据统一计算就是一个很好的应用。
    hantsy
        9
    hantsy  
       2020-12-15 13:36:22 +08:00   ❤️ 1
    @wangyanrui 我了解国内的即使用 JPA 的,很多也是先设计了数据库,用逆向工具(比如 PowerDesigner,Hibernate Tools )反向生成一些 Entity,那么 Entity 类和表几乎 100 % 对应。

    首先逆向工具都是针对 Legacy 程序设计的,对于新项目使用,生成的代码,我真的觉得很恶心。

    良好的 OOP 设计情况下,JPA 只有 Entity 与表(或者视图)对应,JPA 还有其它形式的模型,比如 Embeddable,Entity 中使用 ElementCollection 等。最终能够完全匹配不多。
    hantsy
        10
    hantsy  
       2020-12-15 13:41:49 +08:00
    @Braisdom AWS 是数据库也有用混合引擎,现在好像很多这样的数据库例子。NOSQL 产品太多了,目前还是 Redis,Mongo,Noe4j 几个不同领域的具有代表性。

    Noe4j 之前还兼容 JDBC 的接口,现在 4 。0 以后好像不支持,不过 Neo4j 的 Cypher 查询语言真的强大。
    Braisdom
        11
    Braisdom  
    OP
       2020-12-15 13:47:33 +08:00
    @hantsy 是的,我是做传统线下零售数据系统的,不同类型的数据或不同访问频率的数据用不同的存储方式是很正常的,Cypher 这个我还真得仔细研究一下,Redis 和 Mongo 我都用了,Noe4j 没玩过,值得借鉴
    gowk
        12
    gowk  
       2020-12-15 14:00:23 +08:00 via Android
    那个 orm 男人,他来了
    Braisdom
        13
    Braisdom  
    OP
       2020-12-15 14:02:41 +08:00
    @gowk 你这个 tag 我很喜欢... 过段时间我准备换一个,哈哈
    WhoMercy
        14
    WhoMercy  
       2020-12-15 14:22:17 +08:00   ❤️ 1
    感谢分享经验感悟,这样的推广很不错,👍
    brezp
        15
    brezp  
       2020-12-15 14:54:43 +08:00   ❤️ 2
    楼上说楼主推广自己的东西的, 别人楼主也好歹是带着自己的东西来交流吧, 你反感不点进来不就完事了么?
    cloudhuang
        16
    cloudhuang  
       2020-12-15 15:47:58 +08:00   ❤️ 2
    能讨论总是有意的,不过看了楼主提的例子,有点“为赋新词强说愁”的感觉:

    > 语义表达的双重性
    "一个 Column 可以进行排序、算术运算、比较运算等",这个在 SQL 层面也并非是有 column 自身提供的语义,如果是,那么也就没有 order by, group by......什么事情了。

    > 语法一致性
    我不是太理解,两个完全不等同的东西,讲什么一致性?

    > 也无法 Java 对语义的解析和执行过程进行干预,也就导致了 Java 的表达式无法直接转换为 SQL 表达式,只能以字符串的形式进行拼接
    如果和数据库的交互形式,最终都是 SQL,那么不同的框架,只是拼接的形式不同而已,比如 Hibernate,比如 MyBastis

    > Java 运算符重载
    不知道楼主说的是什么意思,Java 应该是没有运算符重载一说的(C++的运算符重载)
    Braisdom
        17
    Braisdom  
    OP
       2020-12-15 16:17:07 +08:00
    @cloudhuang 当然不是,我再补充一下(后续有必要写一份文档,详细说明):
    1 )语义的双重性:其实就是程序的开发中,同一个编程元素要表达两个不同的含义,例如:一个 Class 中的 Field 在 Java 中是一种类型的数据或状态的载体,例如:class User 中 private String name,从语义上讲,它只能保存 User 实例中的 name 的状态,如果将 User 的实例用于拼接 SQL,这个 name 其实是对应的 users 表中的一个 column, 这种语义的表达,在目前的编程模型中是无法做的,因为 SQL 域中,name 是另一种类型 Column, Column 也有自身的状态和行为,例如:user.name.asc(), user.name.orderBy() 等。这就需要两种不同的定义方式,User, UserDO 两种不同的定义方式表达同一个模型定义, 这也就导致了重复编码和静态生成的变更问题。

    2) 至于 Java 的运算符重载,你可以参考: https://www.v2ex.com/t/734572#reply103
    里面有详细的描述和讨论。
    zhangbohun
        18
    zhangbohun  
       2020-12-15 16:21:20 +08:00 via Android   ❤️ 1
    想看 sql,json,orm 三巨头一起互打广告
    Braisdom
        19
    Braisdom  
    OP
       2020-12-15 16:34:14 +08:00
    @cloudhuang 更直白一点的说,
    class User {
    private String name;
    }

    class UserDO {
    private Column name;
    }

    Java 域中,name 只能用来存储状态,类型为 String
    SQL 域中,name 是另一个类型 Column, Column 中有状态和行为

    传统的方法就是静态生成另一个类 UserDO, 而 ObjectiveSQL 中则不需要,是动态生成。
    Braisdom
        20
    Braisdom  
    OP
       2020-12-15 16:36:54 +08:00
    @zhangbohun 有道理,自己的项目希望更多人知道,开源的肯定不太会花费用去打广告,我也只能用自己的知识去交换眼球,希望能够理解。
    wysnylc
        21
    wysnylc  
       2020-12-15 16:44:58 +08:00
    @neochen13 #3 看的烦
    lidlesseye11
        22
    lidlesseye11  
       2020-12-15 19:54:26 +08:00
    @brezp
    真心求问,不点进来怎么知道是推广?是有啥能直接显示帖子内容的客户端 /插件吗?
    guyeu
        23
    guyeu  
       2020-12-15 21:49:06 +08:00
    确实有点为赋新辞强说愁的感觉;

    楼主为了解决关系型数据库的模型、语义等和 Java 的对象模型不一致的问题,发明了一堆注解和一个 DSL ;
    其他人为了解决同样的问题,发明了一个概念叫对象关系映射,同样的,发明了一堆注解和一个 DSL ( Criteria );

    so,大家都是一样的东西,你这个咋就“完美的解决了上述两个问题”呢。。。
    Braisdom
        24
    Braisdom  
    OP
       2020-12-15 21:55:48 +08:00
    @guyeu 立场不同,说法就不同,经历不同,也会有不同的说法。我只是朴素的阐述自己的理论,ORM 的出现已经 20 多年了,DSL 的理解有很多层,不同人有不同的理解。
    另外说一句,我很不喜欢发明新说法,Java 领域有太多新概念了,“语义表达的双重性” 是我的理解,当然也有其它的说明法,我只是表达一个 Java Field 需要表达两种不同的语义。语法的一致性是一个事实,是我用 Java 的运算符重载解决的特性,大家可以有不同的理解。

    欢迎挑战,最近在推广,我很希望听到不同的意见,感谢各位。
    cloudhuang
        25
    cloudhuang  
       2020-12-15 22:15:55 +08:00
    @Braisdom 这里的 “ SQL 域” 是指什么?是标准 SQL 吗?我不是太理解您说的这种方式。如果是标准 SQL,那么 order 这种方式是通过字句的方式来解决的,其本质其实也是编程语言。
    拿您的例子来说,name 存储为状态这个本身是没有问题的,难道排序后的 name 就不是 name 了?

    回到本质的问题,如果同数据库的交互还是 SQL,那么规避不了拼接 SQL 这个本质,只是的形式不同而已(但形式上确实会有优劣之分,不过会有些主观)。您的这个 ObjectiveSQL 我没有去看,或者采用了其他方式,您可以说明一下,可能 ObjectiveSQL 操作数据库,不是最终在数据库层面去执行 SQL 的,而是通过其他方式。


    > 2) 至于 Java 的运算符重载,你可以参考: https://www.v2ex.com/t/734572#reply103
    里面有详细的描述和讨论。
    cloudhuang
        26
    cloudhuang  
       2020-12-15 22:20:18 +08:00
    > 2) 至于 Java 的运算符重载,你可以参考: 里面有详细的描述和讨论。

    嗯,这个就没啥可说的了,您说这个是运算符重载就是运算符重载吧。但我认为这个是通过函数调用来模拟运算符操作,而运算符(比如+,-,==)重载本质上应该是一种多态。
    Braisdom
        27
    Braisdom  
    OP
       2020-12-15 22:27:16 +08:00
    @cloudhuang
    1 )本质上 ObjectiveSQL 还是 SQL 的拼接,就像所有的应用层网络协议一下,底层还是 TCP/IP,目前我还不能超越 SQL,但我有更深层次的想法,目前还不成熟。

    2 ) SQL 域指的就是传统 SQL 编程,Java Class 中的 name 就有两层含义,一层是表示传统 Java Bean,只能承载 String 类型的值,另一层,就是需要承载作为 SQL 语句抽象后的 Column 含义,它的行为和状态是 String 不具备的,例如:

    User.Table user = User.asTable();
    Select select = new Select();

    select.project(count())
    .from(user)
    .groupBy(user.name.).orderBy(user.name.asc());

    这里的 user.name 其实是 Column 的实例。
    levon
        28
    levon  
       2020-12-15 23:41:59 +08:00   ❤️ 1
    @Braisdom 做 entity framework 的 java 版本,绝对红遍半边天
    bsg1992
        29
    bsg1992  
       2020-12-16 09:12:51 +08:00
    去看看 EF 吧 EF 算是目前实现最好的了
    cloudhuang
        30
    cloudhuang  
       2020-12-16 09:14:38 +08:00
    > 这里的 user.name 其实是 Column 的实例。
    用你的话来解释,那么"user.name",应该是属于 Column 的语义,也不是"name"的语义了,只是恰好你的 column 的名字也叫"name"这个名字。
    Braisdom
        31
    Braisdom  
    OP
       2020-12-16 09:48:14 +08:00
    @levon 您是 V 站的老人,感谢您的鼓励,我会继续努力,不能只停留在到处推广,而是真正的做好项目。
    brezp
        32
    brezp  
       2020-12-16 10:31:03 +08:00
    @lidlesseye11 楼主的头像很有特点
    Braisdom
        33
    Braisdom  
    OP
       2020-12-16 11:08:52 +08:00
    @brezp 是本人的漫画版本
    Rorysky
        34
    Rorysky  
       2020-12-16 17:57:33 +08:00   ❤️ 1
    @neochen13 付出很多的项目,可以理解
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1788 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 16:34 · PVG 00:34 · LAX 08:34 · JFK 11:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.