sql - 用于报告应用程序的动态 SQL

标签 sql jdbc dynamic-sql jooq

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

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

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

最佳答案

您不必使用 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> type 有一个泛型类型 <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

结论:

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

关于sql - 用于报告应用程序的动态 SQL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43586452/

相关文章:

SQL Server 将一个表中的所有行复制到另一个表中,即重复表

c# - NHibernate 中的自然排序

MySQL 返回行的比例子集

mysql - 配置 GlassFish JDBC 连接池以处理 Amazon RDS 多可用区故障转移

sql-server-2008 - SQL 使用变量设置 IDENTITY 字段

sql-server - 使用动态 SQL - 自定义通用多操作存储过程(使用 TVPar)

postgresql - 使用 PostgreSQL 触发器对数据进行动态审计

javascript - 我如何在 SQLite 中进行高级查询以按标签搜索文件?

java - 如何计算数组列表中元素的重复次数?

java - 使用 JDBC 和 c3p0 在 MySQL DB 上进行多线程写入