欢迎来到jOOQ Tuesdays系列。 在本系列中,我们每隔一个月的第三个星期二发布一篇文章,从jOOQ的角度采访我们发现该行业令人兴奋的人。 这包括从事SQL,Java,开放源代码以及其他各种相关主题的人员。
我们很高兴在第八版中与Iu Ming-Yu Iu交谈,他将向我们介绍行业中的不同人如何解决了将查询系统集成到通用语言中的问题,包括他自己的库JINQ ,该库对Java也是如此。 。
是什么让你做到的?
Jinq实际上来自我在瑞士EPFL大学的博士学位研究。 当我于2005年在那开始攻读博士学位时,我需要一个论文主题,并且听说我的主管Willy Zwaenepoel希望简化编写数据库代码的工作。 从1997年当我在IBM JVM团队中的一个实习生开始,我就对Java内部有一定的了解,所以当我研究这个问题时,我是从较低级的系统角度来看的。 结果,我想到了使用字节码重写方案将某些类型的Java代码重写为数据库查询的想法。 同时还有其他研究小组正在研究这个问题, 包括LINQ小组 。 不同的小组根据自己的背景提出了不同的方法。 基本的假设是,由于存在语义鸿沟,程序员难以编写数据库代码-关系数据库模型与面向对象的编程模型是如此不同,以至于程序员浪费了精力来弥补差异。 希望可以通过允许程序员编写普通的Java代码并让计算机确定如何在数据库上运行此代码来减少这种语义上的差距。 不同的方法将导致工具可以处理更复杂的数据库查询,或者在接受的代码样式上更灵活。
尽管我很快就想到了一种初始方法,但我花了很多年时间才将算法改进为更健壮和可用的算法。 与LINQ研究人员相似,我发现我的算法在功能代码方面效果最好。 由于函数式代码没有副作用,因此更易于分析。 向程序员解释如何编写算法仍然可以理解的复杂代码也更加容易。 不幸的是,当我在2010年完成博士学位时,Java仍然没有适当地支持函数式编程,因此我将研究搁置起来以用于其他方面。 但是,当Java 8最终在2014年以lambda出现时,我决定重新审视我的旧研究。 我对研究进行了调整,以利用Java 8 lambda并与当前的企业工具集成。 结果就是Jinq,这是一个开放源代码工具,为Java中的LINQ样式查询提供了支持。
LINQ为什么会出错?
我的看法比这更微妙。 LINQ对于C#生态系统非常有意义,但我认为它完全不适合Java。 不同的语言具有不同的权衡,不同的哲学和不同的历史包bag。 将查询系统集成到Java中会违背Java哲学,并被认为是错误的。 C#在设计时考虑了不同的权衡因素,因此将查询集成之类的功能添加到C#中是可以接受的。
C#旨在快速发展。 C#经常强迫程序员抛弃旧代码,以便它可以采用新的工作方式。 关于软件的Joel上有一篇老文章,描述了Microsoft如何拥有两个阵营 :始终试图保持向后兼容性的Raymond Chen阵营和一直在传播可能在几年后放弃的闪亮的新技术的MSDN Magazine阵营。 Raymond Chen的营地使我可以在Windows 10上运行20年的Windows程序。MSDN Magazine的营地产生了诸如C#和Typescript和LINQ的出色新技术。 MSDN哲学没有错。 许多程序员更喜欢使用使用这种哲学构建的语言,因为API和语言最终会减少它们的遗留问题。 您无需了解API的30年历史即可找出使用它的正确方法。 苹果公司采用了这种理念,尽管许多程序员不得不每隔几年重写所有代码以适应最新的API,但许多程序员还是喜欢它。 对于C#,采用不成熟且仍在发展的技术很好,因为如果无法解决,他们可以稍后放弃。
Java的哲学是永不向后兼容。 1990年代的旧Java代码仍然可以编译,并且可以在现代Java上完美运行。 因此,向Java添加新功能会带来巨大的维护负担。 任何功能都必须保持数十年。 将功能添加到Java后,就无法更改它,否则可能会破坏向后兼容性。 结果,只有那些经过时间考验的功能才可以添加到Java中。 当将尚未完全成熟的功能添加到Java中时,它会“锁定”特定的实现,并阻止该功能随着人们需求的变化而发展。 这可能会在将来引起语言的严重头痛。
这种锁定的一个示例是Java序列化。 能够轻松地将对象写入磁盘非常方便。 但是该功能锁定在一个架构上,不足以适应将来的用例。 人们想将对象序列化为JSON或XML,但不能使用现有的序列化框架来实现。 序列化导致许多安全错误,并且需要大量开发人员资源才能获得lambda和序列化才能正常工作。 这种过早锁定的另一个示例是对所有对象的同步支持。 当时,在语言中内置多线程原语似乎非常具有前瞻性。 由于每个对象都可以用作多线程监视器,因此您可以轻松地同步对每个对象的访问。 但是我们现在知道,好的多线程程序可以避免这种细粒度的同步。 最好使用更高级别的同步原语。 所有这些低级同步都会降低单线程和多线程代码的性能。 即使您不使用该功能,所有Java对象也必须由拥有锁支持的开销所负担。 最好是将序列化和同步都添加到Java中。 但是现在这些功能被视为“ goto”:它们未通过气味测试。 如果您看到使用这些功能的任何代码,则通常意味着该代码需要额外的审查。
向Java添加LINQ样式的查询可能会导致类似的问题。 不要误会我的意思。 LINQ是一个很棒的系统。 它是目前我们用于将查询语言集成为面向对象语言的最完善的系统。 许多人喜欢使用C#,特别是因为LINQ。 但是底层技术仍然太不成熟,无法添加到Java中。 研究人员仍在提出将查询系统嵌入语言的更新更好的方法,因此存在将Java锁定为一种后来被认为过时的方法的真正危险。 研究人员已经对LINQ进行了许多改进,而Microsoft在不放弃其旧代码的情况下无法采用。
例如,为了将LINQ表达式转换为数据库查询,Microsoft在C#中添加了一些功能,使LINQ可以在运行时检查lambda表达式的抽象语法树。 这个功能很方便,但是它限制了LINQ只使用表达式。 LINQ无法使用语句,因为它无法检查包含语句的lambda的抽象语法树。 对可以检查哪种类型的lambda的限制非常微不足道。 尽管此检查lambda的功能确实非常强大,但由于其功能受到限制,因此很少有其他框架使用它。 在通用编程语言中,所有语言原语应具有足够的表现力,以使其可用作许多不同结构和框架的构建块。 但是这种lambda检查功能最终仅对LINQ这样的查询框架有用。 实际上,Jinq已经表明此功能甚至不是必需的。 可以仅使用编译后的字节码来构建LINQ风格的查询系统,而最终的查询系统将变得更加灵活,因为它可以处理语句和其他命令式代码结构。
随着程序员在LINQ上获得了更多的经验,他们也开始怀疑是否存在替代方法比LINQ更好。 LINQ可以使程序员更轻松地编写数据库查询,因为他们可以编写功能样式的代码,而不必学习SQL。 但是实际上,要很好地使用LINQ,程序员仍然还需要了解SQL。 但是,如果程序员已经了解SQL,那么LINQ有什么优势? 使用像jOOQ这样的查询系统比使用Slick更紧密地匹配SQL语法,然后可以Swift发展以包含新的SQL功能会更好吗? 也许,查询系统甚至不是必需的。 越来越多的公司正在采用甚至根本不支持查询的NoSQL数据库。
鉴于我们对LINQ样式查询系统的理解发展的速度有多快,目前将功能直接添加到Java这样的语言中肯定是错误的。 任何方法都可能最终被淘汰,这将对Java的未来版本造成很大的维护负担。 幸运的是,Java程序员可以改用Jinq和jOOQ之类的库,它们提供了LINQ的大部分优点,但不需要像LINQ这样的紧密语言集成。
JINQ与Slick相比如何?
他们都试图提供LINQ风格的接口来查询数据库。 由于Slick是为Scala设计的 ,因此它具有出色的Scala集成,并且能够使用Scala更具表现力的编程模型来提供非常优雅的实现。 为了获得Slick的全部好处,您必须拥抱Scala生态系统。
Jinq主要设计用于Java。 它与现有的Java技术(如JPA和Hibernate)集成。 采用Jinq时不必放弃现有的Java企业代码,因为Jinq可与现有的JPA实体类一起使用。 Jinq专为逐步采用而设计。 您可以在某些地方有选择地使用它,而在其他地方则可以使用常规的JPA代码。 尽管Jinq可以与Scala一起使用,但对于使用Scala但尚未采用完整的Scala生态系统的组织而言,它更有用。 例如,Jinq允许您在Scala代码中使用现有的Hibernate实体,同时仍对它们使用现代LINQ风格的功能查询系统。
您对Java中的函数式编程有何看法?
我很高兴Java终于支持lambda。 巨大的进步确实使我作为程序员的生活变得更加轻松。 随着时间的流逝,我希望Java语言管理员能够进一步优化lambda。
从Jinq的角度来看,Java 8的lambda的主要弱点之一是完全缺乏任何反射支持。 Jinq需要反射支持来解码lambda并将其转换为查询。 由于没有反射支持,因此Jinq需要使用缓慢而脆弱的替代技术来获取相同的信息。 就我个人而言,我认为缺少反射是一个重大的疏忽,而从长远来看,缺少反射支持可能会削弱整个Java生态系统的整体 。
我有些烦恼,缺少注释支持,也缺少有关如何处理lambda的良好JavaDoc指南。 对我来说,Streams API和lambda元工厂似乎也有些复杂,我想知道是否有更简单的方法会更好。
但是从日常编程的角度来看,我发现缺少用于调用lambda的语法糖是反复困扰我的主要问题。 这似乎是一件相当小的事情,但是我使用lambda的次数越多,我就越觉得它真的很重要。 在Java 8中,创建和传递lambda很容易,我通常可以完全忽略使用单个方法将lambda表示为类的事实。 我可以用lambda来考虑我的代码。 当我编写Java 8代码时,我的思维模式是创建lambda并将其传递。 但是,当我实际上不得不调用lambda时,lambda的魔力就完全崩溃了。 我必须停下来换档,并从类的角度考虑lambda。 就我个人而言,我永远都不记得为调用lambda而需要调用的方法的名称。 它是run(),accept(),consume()还是apply()? 我常常最终不得不查找方法名称的文档,这打乱了我的注意力。 如果Java 8具有用于调用lambda的语法糖,那么我将不需要突破lambda抽象。 我将能够创建,传递和调用lambda,而不必将其视为类。
您打算实施被动式JINQ吗?
老实说,我对反应式API不太熟悉。 最近,我主要从事桌面应用程序的工作,因此,我不必在反应性方法有意义的足够范围内处理问题。
你目前在做什么?
一段时间后,很容易积累项目。 尽管我偶尔会添加错误修复和其他更改,但Jinq目前基本上是稳定的。 仍然可以添加一些主要功能,例如对批量更新的支持或改进的代码生成,但是这些都是相当主要的工作,需要一些资金才能完成。
我有时会使用一种称为Babylscript的编程语言,这是一种多语言编程语言,可让您以法语,中文,阿拉伯语和其他非英语语言混合编写代码。 作为该项目的配套项目,我还经营了一个网站,用于向孩子们教授编程知识,该网站名为“编程基础知识”,该语言以17种不同的语言进行教学。 不过,目前,我大部分时间都花在两个项目上。 一种是称为Omber的美术工具,这是一个专门绘制高级渐变的矢量绘图程序。 另一个项目涉及使用HTML5作为桌面Java程序的UI前端。 您的所有UI代码仍将用Java编写,但是您无需使用AWT或Swing,而只需使用绑定到Java的标准DOM接口来操作HTML。 附带的好处是,您可以使用GWT将所有UI代码重新编译为JavaScript,因此您也可以将UI代码重用于网页。
进一步的信息
Ming,非常感谢您接受这次非常有趣的采访。 想更多地了解JINQ? 在jOOQ博客上的前一个嘉宾帖子中阅读有关此内容的内容 ,并观看Ming的JVMLS 2015演讲: