比较Querydsl、jOOQ、JEQUEL、activejdbc、iciql和其他查询DSL

34

请问有没有关于使用Java的不同Query DSL库的性能比较的资源,例如:QuerydsljOOQJEQUELactivejdbciciql等等……

背景:我正在使用Spring JDBC模板,但这仍然需要以纯字符串格式编写查询。虽然我在直接编写查询方面没有问题,但我担心直接依赖DB表名。我不想使用任何ORM框架,如Hibernate或JPA/EclipseLink。我需要尽可能高的原始性能(在我看来,它们适用于更多的CRUD中心应用程序)。我只能承受这些DSL的一些轻微开销,如果那只是一点点的话(我相信,它们主要是StringBuilder/String连接!)

我考虑过在某些xml中外部化命名查询。但只是试图评估不同Query DSL库提供的价值。

编辑:更多关于我的要求: 我想知道在使用它们的API方法构建一个中等复杂查询时,它们之间的性能比较。我只需使用这些查询DSL库中的任何一个生成查询字符串,并将其传递给Spring JDBC模板即可。因此,我想知道添加这个中间步骤是否会带来相当大的性能损失,如果是,我想使用命名查询或构建自己的库,该库仅使用StingBuilder或类似方法

更新我对jOOQ、iciql和QueryDSL的经验:

虽然我在原始帖子中没有提到这一点,但我也非常关心易用性和实体类中需要的开销(例如是否需要任何其他注释或实现)。

jOOQ:

  • 需要将实体属性更改为库特定方式
  • 可以返回SQL查询字符串

Iciql:

  • 实体可以映射为无需或少量更改的方式(可以使用总共3种方式进行映射)
  • 但这限制了仅使用选择查询(对于更新/删除/...需要再次更改实体)

QueryDSL:

  • 绑定实体与表的多种方法(除了库特定方式外,还支持使用JPA注释)。但我们至少需要修改实体
  • 没有简单/直接的方法来获取查询字符串

(所有观察结果都是基于我对这些的了解;如果有任何不正确的地方,请纠正)

基于以上所有内容,我仍然坚持编写命名查询:(但是,由于Lukas Eder的答案似乎解释了我的原始帖子的关注点(性能),因此我已经接受了他的答案。


你想要使用相同的基础SQL查询进行比较吗?这是一个非常困难的任务,需要考虑处理、投影、生成的SQL以及各种JDBC级别优化的使用。 - Timo Westkämper
我已经编辑了问题,请查看。我不想要任何像ORM这样自己发出查询的功能。对于库来说,生成查询就足够了。谢谢。 - manikanta
@mrCoder:下一次,与其修改您的问题(可能会让某些人感到困惑),如果您想进一步阐述,也可以向自己的问题添加答案。 - Lukas Eder
从QueryDSL获取查询字符串的方法在这里描述:https://dev59.com/TGEi5IYBdhLWcg3wFokL - Dan Halbert
4个回答

30

在现代JVM中,你不需要太担心SQL字符串拼接。与相对较高的往返数据库时间相比,任何数据库抽象层可能产生的真正开销通常是由于Hibernate/JPA中的二级缓存或通过以使使用索引或通用查询转换变得不可能地将对象模型不高效地映射到SQL。

与此相比,即使是具有多个UNIONs、嵌套SELECTsJOINssemi-JOINsanti-JOINs等复杂SQL结构,字符串连接也是微不足道的,因此我猜测你提到的所有框架表现方式相似,因为它们都允许你控制你的SQL。

另一方面,这些框架中的某些框架或使用模式实际上可能会将整个结果集获取到内存中。如果你的结果集很大,这可能会导致问题,而且由于Java的泛型,大多数基本类型(如intlong等)可能会被映射到它们对应的包装器类型(如IntegerLong)。

至于我开发的jOOQ,我以前使用YourKit Profiler对库进行了大量查询执行分析。大部分工作都是在数据库中完成的,而不是在查询构造中完成的。jOOQ为每个查询使用单个StringBuilder。我想象(未经验证),QueryDSLJEQUEL也是这样做的...

关于iciql,它是JaQu的一个分支,他们使用Java仪表来反编译他们的自然语法,这可能会产生一些额外影响。但我想如果这意味着影响太大,那么可以忽略它。


3
我同意Lukas的观点,Querydsl在大多数查询中也使用单个StringBuilder,字符串构建开销非常小。由于创建中间DSL对象和相关垃圾收集所产生的开销很难正确测量。 - Timo Westkämper
1
嗨Timo! :-) 我认为中间对象的GC是边缘化的。这些对象永远不会进入堆(老年代)。那么大型结果集呢?QueryDSL是否像jOOQ在默认执行模式下一样将结果加载到内存中?jOOQ支持延迟获取,但我认为这并不经常使用。QueryDSL是否支持结果集中的原始类型? - Lukas Eder
4
Querydsl 支持通过迭代实现急切和延迟获取。Querydsl 支持结果集中的原始类型。 - Timo Westkämper
感谢您详细的回答。这是否意味着,jOOQ和QueryDSL都不仅仅是查询构建器?如果是的话,我可以在Spring JDBC模板中使用它们吗(这意味着它们可以通过某种方法发出生成的查询字符串,我可以将其传递给模板方法吗?) - manikanta
2
@mrCoder:是的,这两个框架也用于执行查询,尽管这一步骤是可选的 - 如果您希望将它们用作纯查询构建器。据我所知,JEQUEL不是这种情况。在jOOQ中,可以使用Query#getSQL()获取SQL。当然,Timo马上会告诉你如何使用QueryDSL完成这项工作... - Lukas Eder
实际上,在Querydsl中这并不是那么简单。使用query.toString(),您将获得一个预处理语句的模板,但绑定并不直接可访问。Spring Data提供了与Querydsl的集成:http://www.springsource.org/node/3192 - Timo Westkämper

6

您还应该查看MyBatis语句构建器

虽然MyBatis显然是一种映射技术,但它确实具有一个语句构建器DSL,似乎与MyBatis解耦(也就是说,您不需要从MyBatis中获取其他任何东西来使用构建器...令人恼火的是它不在自己的jar包中)。我不喜欢它,因为它使用ThreadLocals。


1
+1 针对该 API 的想法!尽管这允许构造毫无意义的虚假查询,没有任何框架支持语法正确性。我猜,ThreadLocal 也是此 API 的唯一可能选择。如果你忘记了 BEGIN(),请小心... - Lukas Eder
我也无法相信ThreadLocal。我想使用JOOQ,但我更喜欢使用真正的SQL和一些常量,并将其映射到不可变对象,并基于Spring JDBC构建它,以便我可以使用@Transaction。我现在唯一缺少的就是模式表/列名称常量生成器。希望我不会在越南迷路 :) - Adam Gent

2

我无法代表其他框架,但我进行了一项基本的性能分析,比较了ActiveJDBC和Hibernate。测试是在一台带有8G RAM、SSD驱动器的笔记本电脑上对MySQL进行的。表PEOPLE有几个简单的列和一个替代ID PK。

其中一个测试是将50K条记录作为对象插入,另一个测试是一次从表中读取50K个对象(在内存中)。在这两个测试中,ActiveJDBC相对于Hibernate表现出了40%的性能提升。在任何情况下,生成的查询都是简单的插入和选择,非常相似。

希望这可以帮到你,

Igor


ActiveJDBC不是一个查询生成框架,而是一个ORM。因此,您无法从中获取生成的查询以供Spring JDBC模板使用。 - ipolevoy
不错。这应该展示了巨大的抽象层和二级缓存的影响。你在测试期间启用了它吗? - Lukas Eder
不,这些测试主要是为了暴露对象创建问题。由于代码不会运行相同的查询两次,使用缓存不会产生性能上的好处(猜测)。AJ具有查询级缓存:http://code.google.com/p/activejdbc/wiki/Caching,我确信它的行为与Hibernate非常不同。我所做的是一项残酷的力量测试,苹果对苹果。 - ipolevoy
好的,我可以看出在某些情况下缓存可能会有所帮助。不错的想法! - Lukas Eder

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接