【问题标题】:Dynamic SQL for Reporting App报告应用程序的动态 SQL
【发布时间】:2017-09-21 00:55:13
【问题描述】:

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

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

虽然我真的很喜欢编写类型安全查询(使用 jOOQ 代码生成)的想法,但我想对于我的用例来说,这不是最合适的。因为表、列等是完全未知的,我想我只需要 jOOQ SQL 构建器。这意味着我必须放弃类型安全。我的评估是否正确?或者是否有任何模式可以用于构建“动态”SQL 而不会影响类型安全?任何指针将不胜感激。

【问题讨论】:

    标签: sql jdbc dynamic-sql jooq


    【解决方案1】:

    您不必使用 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();
    

    您也可以通过“功能性方式”执行此操作:

    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,恰好也适用于静态查询。

    【讨论】:

      猜你喜欢
      • 2012-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-07
      相关资源
      最近更新 更多