报告应用程序的动态SQL

时间:2017-04-24 11:06:43

标签: sql jdbc dynamic-sql jooq

即将开始使用jOOQ进行概念验证。 jOOQ看起来非常简单,富有表现力,使SQL维护变得更加容易。

我们是一家Java 8商店。这里的用例是为报表应用程序编写数据层,该应用程序根据屏幕上的用户选择动态查询表,列,过滤器和函数。

虽然我非常喜欢编写类型安全查询(使用jOOQ codegen)的想法,但我认为对于我的用例,它不是最合适的。因为表,列等是完全未知的,我想我只需要jOOQ SQL构建器。这意味着我必须放弃类型安全。我的评估是否正确?或者我是否可以使用任何模式来构建"动态" SQL在不影响类型安全的情况下?任何指针都会非常感激。

1 个答案:

答案 0 :(得分:2)

您不必使用jOOQ的代码生成器来充分利用jOOQ中的大多数功能。手册的介绍部分指出,jOOQ可以很容易地用作SQL构建器,而不需要代码生成器提供的额外类型静态安全性:

https://www.jooq.org/doc/latest/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder

代码生成提供的类型安全性

代码生成器本质上提供了两种类型的安全元素:

  • 对象名称硬连接到类名(表,模式,序列,数据类型,过程等)和属性名称(列,类型属性,过程参数)。
  • 属性类型(列,属性,参数)硬连线到通用属性定义中。

这些确实有助于开发您的应用程序

jOOQ API

提供的类型安全性

...但要注意代码生成器只是反向设计架构的静态快照。它是类型安全的,因为整个jOOQ API允许这种类型的安全性。例如,Field<T>类型具有泛型类型<T>,其也可以在没有代码生成器的情况下使用,例如,使用plain SQL APIs

Field<String> firstName = field(name("USER", "FIRST_NAME"), SQLDataType.VARCHAR(50));

上述API用法(DSL.field(Name, DataType))与代码生成器的用法大致相同。它创建一个列引用,其中附加了列类型信息。您可以像代码生成器生成的列一样使用它:

DSL.using(configuration)
   .select(firstName)
   .from(name("USER"))
   .where(firstName.like("A%")) // Compiles
   .and(firstName.eq(1))        // Doesn't compile: firstName must be compared to String
   .join(name("ADDRESS"))       // Doesn't compile: the SQL syntax is wrong
   .fetch();

正如您所看到的,与使用代码生成器相比,唯一改变的是表/列引用。

动态SQL

但这意味着,没有代码生成器,jOOQ对你来说更加强大。您仍然可以非常轻松地创建动态SQL语句。例如:

// Construct your SQL query elements dynamically, and type safely
Condition condition = hasFirstNameFilter()
    ? firstName.like("A%")
    : DSL.trueCondition();

DSL.using(configuration)
   .select(firstName)
   .from(name("USER"))
   .where(condition)     // Use dynamically constructed element here
   .fetch();

您也可以使用&#34;功能方式&#34;:

DSL.using(configuration)
   .select(firstName)
   .from(name("USER"))
   .where(condition())   // Call a function to create the condition here
   .fetch();

甚至更好

public static Select<Record1<String>> firstNames(
    Function<? super Field<String>, ? extends Condition> condition
) {
    return
    DSL.using(configuration)
       .select(firstName)
       .from(name("USER"))
       .where(condition.apply(firstName)); // Lazy evaluate the predicate here
}

// Use it like this:
firstNames(col -> col.like("A%")).fetch();

甚至更好,使上面的更高阶函数:

public static Function<
    ? super Function<? super Field<String>, ? extends Condition>, 
    ? extends Select<Record1<String>>
> firstNames() {
    // Lazy construct query here
    return f -> DSL.using(configuration)
                   .select(firstName)
                   .from(name("USER"))
                   .where(f.apply(firstName)); // Lazy evaluate the predicate here
}

// Use it like this:
firstNames().apply(col -> col.like("A%")).fetch();

此处有更多详情: https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql

结论:

正如您所看到的,虽然代码生成器确实为静态模式添加了很多值,但在jOOQ API中没有什么是静态的。 jOOQ是一个用于动态SQL查询构造的API,它恰好也适用于静态查询。