是否可以结合MyBatis和QueryDSL / jOOQ?

MyBatis提供了映射,本地缓存和开箱即用的日志记录。
因此,QueryDSL / jOOQ提供SQL语句的编译时检查和IDE自动完成。
是否可以将它们结合起来?

换句话说, 我想用QueryDSL或jOOQ创建一个查询,然后使用MyBatis的一些粘合代码/适配器执行它

我已经检查过:

  • 我考虑使用QueryDSL生成SQL查询字符串,并在MyBatis中使用它们的’@SelectProvider’注释,但它似乎是一个死胡同:MyBatis在其SQL字符串中需要“$ {xxx}”,但QueryDSL只生成查询基于实际的Java类型,因此即使对于ID也不起作用。
  • MyBatis Generator作为QueryDSL / jOOQ的替代品:非常差的替代方案,因为它实际上生成了一个样板代码,您以后必须维护和扩展它
  • MyBatis SQL Builder作为QueryDSL / jOOQ的替代品:比QueryDSL或jOOQ弱得多,例如它不提供列名的编译时检查,它更麻烦,并且它依赖于使代码复杂化的’@SelectProvider’

我将从高层回答 – 所以我不是在深入研究QueryDSL和jOOQ之间的实际差异,在讨论的这一点上,“只是”在Java中提供了类型安全的嵌入式SQL。 但是,我会从jOOQ的角度给出答案,因为我知道API要好得多。

免责声明:我是从为jOOQ背后的公司工作的人的角度给出这个答案的。

是的你可以:

是的,您可以将jOOQ与MyBatis结合使用。 例如,您可以从jOOQ查询中提取查询字符串,如下所示:

Select query = DSL.using(configuration) .select(TABLE.A, TABLE.B) .from(TABLE) .where(TABLE.C.eq(1)); String sql = query.getSQL(); List bindvalues = query.getBindValues(); 

许多人实际上使用这种技术然后使用Spring JDBC而不是jOOQ运行查询。 他们为什么这么做的主要原因是因为他们已经广泛使用了Spring JDBC,并且他们不希望在他们的堆栈上有两种类型的查询执行。

但你为什么要这样:

您添加到堆栈中的每个API也会增加堆栈行为的复杂性和一组规则。 另一方面,您有一组要从这些API实现的function。 让我们确定这些function:

  1. 键入安全的嵌入式SQL
  2. 高速缓存
  3. 记录
  4. 将非规范化SQL结果映射到您的域

1)类型安全的嵌入式SQL

这是一个明智的做法。 你可能不想使用MyBatis。 这种实现(正如您所发现的)更像是一个概念certificate。 所以你选择了jOOQ

2)缓存

这是我个人认为你太快就得出结论的东西。 MyBatis有很多非常简单的实现,但是在缓存的情况下,我很确定你想要实现一个更通用的缓存策略,例如使用新的JSR-107缓存支持,就像Spring中的那个一样因为缓存并不真正与SQL紧密耦合。

3)记录

例如,jOOQ实现了很容易挂钩到日志记录挂钩 ,但您也可以使用JDBC跟踪日志库或JDBC驱动程序的function来使用 JDBC级别的日志记录

4)映射

我说过缓存的内容在这里也是如此。 MyBatis为您的映射算法提供了一个简单的默认实现,当您转向更复杂的映射方案时,这可能很快就不够了。 对于jOOQ,btw也是如此,它也实现了POJO的默认映射 , 您可以以任何方式覆盖它 。 但很像缓存,映射并不是一个应该在SQL级别上解决的问题。 你会发现更好的工具用于映射本身 – 例如Model Mapper ( 内置jOOQ支持 ,顺便说一下 )。 或者如果你在Java 8环境中,你可以使用常规的函数编程技术来映射东西 ,例如:

 DSL.using(configuration) .select( COLUMNS.TABLE_NAME, COLUMNS.COLUMN_NAME, COLUMNS.TYPE_NAME ) .from(COLUMNS) .orderBy( COLUMNS.TABLE_CATALOG, COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME, COLUMNS.ORDINAL_POSITION ) .fetch() 

上图:jOOQ代码。 下图:Java 8映射代码

  .stream() .collect(groupingBy( r -> r.getValue(COLUMNS.TABLE_NAME), LinkedHashMap::new, mapping( r -> new Column( r.getValue(COLUMNS.COLUMN_NAME), r.getValue(COLUMNS.TYPE_NAME) ), toList() ) )) .forEach( (table, columns) -> { System.out.println( "CREATE TABLE " + table + " ("); System.out.println( columns.stream() .map(col -> " " + col.name + " " + col.type) .collect(Collectors.joining(",\n")) ); System.out.println(");"); } ); 

这是本文底部的示例,它显示了如何查询所有表的H2 INFORMATION_SCHEMA ,并将结果映射到CREATE TABLE语句

结论:

许多API倾向于引诱您使用它们,因为它们具有非核心“function”,例如缓存或映射,这些function实际上是SQL API的非核心function。 您将通过简单的用例和宠物/猫/狗或作者/书籍应用程序快速启动并运行,但您将陷入更复杂的困境。 在您的用例中,类型安全的嵌入式SQLfunction是您想要使用类似jOOQ的原因。 您正在寻找的其他function都不是非常令人信服的使用MyBatis的原因(其核心function是外部SQL文件,这与嵌入SQL的jOOQ完全相反)。

所以,我给你的建议是:找出你想要在堆栈中使用的每个function的最佳工具,看看这些工具是否易于组合。 我们在实现jOOQ时非常谨慎,以允许任何第三方应用程序非常容易地插入jOOQ(例如缓存,日志记录,映射),因为这些function不是jOOQ的核心function。

就像给Lukas添加一个非常完整的答案。

从我的基准测试来看,MyBatis在映射方面存在一些严重的性能问题,Jooq作为一个更好的性能曲线。 那是Jooq在mapper上的问题。

https://github.com/arnaudroger/SimpleFlatMapper#local-mysql

在此处输入图像描述

SimpleFlatMapper也作为与querydsl和spring数据的集成点。 我不知道你可以将自己的映射与Jooq集成,并将其添加到积压中。