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

Posted

技术标签:

【中文标题】比较 Querydsl、jOOQ、JEQUEL、activejdbc、iciql 和其他查询 DSL【英文标题】:Comparing Querydsl, jOOQ, JEQUEL, activejdbc, iciql and other query DSLs 【发布时间】:2011-11-06 17:29:47 【问题描述】:

谁能指出一些关于可用于 Java 的不同 Query DSL 库之间性能比较的资源,例如:QuerydsljOOQJEQUELactivejdbciciql等等……

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

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

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

更新我对 jOOQ、iciql、QueryDSL 的体验:

虽然我在原始帖子中没有提及这一点,但我也很喜欢我的实体类中需要的易用性和开销(比如是否需要任何额外的注释或实现)。

jOOQ:

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

Iciql:

可以在没有或很少更改的情况下映射实体(总共可以使用 3 种方式进行映射) 但它仅限于选择查询(更新/删除/...需要再次更改实体)

查询DSL:

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

(所有观察都是我对这些知之甚少;如果其中任何一个不正确,请更正)

综上所述,我坚持编写命名查询 :( 但正如 Lukas Eder 的回答似乎解释了我最初的帖子关注点(性能),我接受了他的。

【问题讨论】:

您是否想要与相同的底层 SQL 查询进行比较?这是一项相当艰巨的任务,您需要考虑到处理、预测、生成的 SQL 以及各种 JDBC 级别优化的使用。 我已经编辑了这个问题。请参见。我不想要 ORM 之类的任何功能或自行发出查询的东西。库生成查询就足够了。谢谢 @mrCoder:下次不要修改您的问题(这可能会让某些人感到困惑),如果您想进一步详细说明,您也可以为自己的问题添加答案。 . 从 QueryDSL 获取查询字符串的描述如下:***.com/questions/21689222/… 【参考方案1】:

在现代 JVM 中,您不必过多担心 SQL 字符串连接。任何数据库抽象层可能产生的真正开销(与到数据库和返回的相对较高的往返时间相比)通常是由于二级缓存,这是在 Hibernate/JPA 中完成的。或者通过使用索引或一般查询转换变得不可能的方式将对象模型映射到 SQL 的效率低下。

与此相比,字符串连接实际上可以忽略不计,即使对于具有多个 UNIONs、嵌套 SELECTsJOINssemi-JOINsanti-JOINs 等的复杂 SQL 构造也是如此,所以我猜都是您提到的框架以类似的方式执行,因为它们允许您保持对 SQL 的控制。

另一方面,某些框架或这些框架中的使用模式实际上可能会将整个结果集提取到内存中。如果您的结果集很大,这可能会导致问题,也因为使用 Java 的泛型,大多数原始类型(intlong 等)可能会映射到它们相应的包装器(IntegerLong)。

至于jOOQ(我是其中的开发人员),我之前已经使用YourKit Profiler 对库进行了概要分析,以执行大量查询。批量工作总是在数据库中完成,而不是在查询构造中。 jOOQ 对每个查询使用一个 StringBuilder。我想(未验证),QueryDSL 和 JEQUEL 做同样的事情......

至于iciql,它是JaQu 的一个分支,他们使用Java 工具来反编译他们的natural syntax 可能会产生一些额外的影响。但我想这可以省略,如果它意味着太大的影响。

【讨论】:

我同意 Lukas 的观点,Querydsl 对大多数查询也使用单个 StringBuilder,并且 String 构建开销很小。创建中间 DSL 对象和相关垃圾回收所产生的开销很难正确衡量。 嗨蒂莫! :-) 我认为中间对象上的 GC 也是微不足道的。这些对象永远不会进入堆(永久空间)。大型结果集如何? QueryDSL 是否像 jOOQ 在默认执行模式下那样将结果加载到内存中? jOOQ 支持延迟获取,但我认为这并不经常使用。 QueryDSL 是否支持结果集中的原始类型? Querydsl 通过迭代支持急切和惰性获取。 Querydsl 支持结果集中的原始类型。 感谢您的详细回答。这是否意味着 jOOQ 和 QueryDSL 不仅仅是查询构建器?如果是,我可以将它们与 Spring JDBC 模板一起使用(这意味着,它们可以通过一些我可以传递给模板方法的方法发出生成的查询字符串吗?) @mrCoder:是的,这两个框架也用于执行查询,即使该步骤是可选的 - 如果您希望将它们用作普通查询构建器。据我了解,JEQUEL 并非如此。在jOOQ中,可以使用Query#getSQL()获取SQL。当然,Timo 会在一分钟内告诉您如何使用 QueryDSL 来完成...【参考方案2】:

你也应该看看MyBatis Statement Builder。

虽然 MyBatis 显然是一种映射技术,但它确实有一个 Statement builder DSL,它似乎与 MyBatis 分离(也就是说,您不需要 MyBatis 的任何其他东西来使用构建器......令人讨厌的是它不在自己的 jar 中)。我不喜欢它,因为它使用 ThreadLocals。

【讨论】:

+1 为那个 API 想法!虽然,这允许构建根本没有意义的虚假查询,而没有任何框架支持语法正确性。我想,ThreadLocal 也是这个 API 唯一可能的选择。请注意,如果您忘记了BEGIN()... I could not believe the threadlocal either 。我想使用 JOOQ,但我更喜欢使用只有一些常量的真正 SQL,并将它映射到不可变对象,并且它是基于 Spring JDBC 构建的,因此我可以使用@Transaction。我现在唯一缺少的是模式表/列名常量生成器。 I'm hoping I don't get lost in vietnam :)【参考方案3】:

我不能代表其他框架,但我对性能进行了原始分析以比较 ActiveJDBC 和 Hibernate。测试是在具有 8G RAM、SSD 驱动器的笔记本电脑上针对 mysql 进行的。带有几个简单列和代理 ID PK 的表 PEOPLE。

一个测试是插入 50K 记录作为对象,另一个是一次从表中读取 50K 对象(在内存中)。在这两项测试中,ActiveJDBC 都显示出比 Hibernate 提高 40% 的性能。在任何一种情况下,生成的查询都是简单的插入和选择,彼此非常相似。

希望这会有所帮助,

伊戈尔

【讨论】:

ActiveJDBC 不是查询生成框架,这是一个 ORM,因此您无法从中获取生成的查询以输入 Spring JDBC 模板 不错。这应该显示出巨大的抽象层的影响,也许还有二级缓存。您是否在测试期间启用了该功能? 不,这些测试主要是为了暴露对象创建问题。由于代码不会两次运行相同的查询,因此使用缓存不会产生性能优势(推测)。 AJ 有一个查询级缓存:code.google.com/p/activejdbc/wiki/Caching,我敢肯定它的行为与 Hibernate 完全不同。我所做的是一个残酷的力量测试,苹果对苹果。 好的,我可以看到缓存在某些情况下可能会有所帮助。好主意!【参考方案4】:

OpenHMS SQL Builder 库是一个用于编程 SQL 查询创建的轻量级、无依赖库:

https://openhms.sourceforge.io/sqlbuilder/

可作为 Maven 依赖项使用:

https://mvnrepository.com/artifact/com.healthmarketscience.sqlbuilder/sqlbuilder

【讨论】:

以上是关于比较 Querydsl、jOOQ、JEQUEL、activejdbc、iciql 和其他查询 DSL的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有域类的 querydsl 中构造查询

我想在数据库环境是动态的服务器上使用 jooq

Spring - QueryDsl 支持的高级比较器

SpringBoot12 QueryDSL02之利用QueryDSL实现多表关联查询

jOOQ:比较 uuid(来自 postgresql)和字符串(类中的用户 id)

jooq 比较 2 个日期 - 它不喜欢 java.sql.Date