用于“的工具”;“内部化”;在Java中使用外部DSL

用于“的工具”;“内部化”;在Java中使用外部DSL,java,dsl,dsl-tools,Java,Dsl,Dsl Tools,我正在开发和维护一个名为的数据库抽象库,其目的是将SQL作为外部DSL“内部化”到Java中。这项工作的目标是允许类型安全地构造和执行最流行的RDBMS的所有可能的SQL语法元素。jOOQ的内部DSL变得越来越复杂,我想正式掌握它。我的想法是,我希望能够有某种形式的SQL定义作为输入,例如 select ::= subquery [ for-update-clause ] subquery ::= SELECT [ { ALL | DISTINCT | UNIQUE } ] select-lis

我正在开发和维护一个名为的数据库抽象库,其目的是将SQL作为外部DSL“内部化”到Java中。这项工作的目标是允许类型安全地构造和执行最流行的RDBMS的所有可能的SQL语法元素。jOOQ的内部DSL变得越来越复杂,我想正式掌握它。我的想法是,我希望能够有某种形式的SQL定义作为输入,例如

select ::= subquery [ for-update-clause ]
subquery ::= SELECT [ { ALL | DISTINCT | UNIQUE } ] select-list 
           [ FROM table-reference ] ..
select-list ::= expression [ [ AS ] alias ] [, expression ... ]
expression ::= ...
alias ::= ...
table-reference ::= ...
输入也可以用XML或任何其他描述性元语言定义。一旦我有了这个输入,我想从这个输入中生成一组Java接口,用Java建模定义的语法。接口示例如下:

// The first "step" of query creation is modelled with this interface
interface Select0 {

    // The various SELECT keywords are modelled with methods
    // returning the subsequent generated syntax-element
    Select1 select(Expression...);
    Select1 selectAll(Expression...);
    Select1 selectDistinct(Expression...);
    Select1 selectUnique(Expression...);
}

// The second "step" of query creation is optional, hence it
// inherits from the third "step"
interface Select1 extends Select2 {

    // Here a FROM clause may be added optionally
    Select2 from(TableReference...);
}

// To keep it simple, the third "step" is the last for this example
interface Select2 extends SelectEnd {
    // WHERE, CONNECT BY, PIVOT, UNPIVOT, GROUP BY, HAVING, ORDER BY, etc...
}
通过上述接口,可以用Java构建SQL查询,就像jOOQ现在已经允许的那样:

create.select(ONE, TWO).from(TABLE)...
create.selectDistinct(ONE, TWO).from(TABLE)...
// etc...
另外,我想排除一些特定构建的语法元素。例如,当我构建jOOQ与MySQL一起专用时,不需要支持SQL MERGE语句


是否有任何现有的库实现这样一种通用的方法,以便正式地将DSL内部化和外部化到Java?还是我自己动手?

您真正想做的是将通用SQL转换为对内部API的调用。看起来很合理

要做到这一点,您需要“通用SQL”的解析器,以及从该解析器生成代码的方法。通常,您需要解析器来构建一个抽象语法树,您几乎需要一个符号表(这样您就可以知道什么是表名,什么是列名,以及这些列名是来自表a还是表B,所以在某些地方您需要访问定义数据模型的SQL DDL…这需要您再次解析SQL:)

使用AST和symbol表,可以以多种方式生成代码,但一种简单的方法是在遇到AST和翻译结构时遍历它们。这不允许构建优化的查询;这需要更复杂的代码生成,但如果得到您提供的适当API函数的支持,我认为这是足够的

实际的代码生成可以通过打印Java文本来完成;如果你走ANTLR路线,你将不得不这样做。另一种方法是将SQL代码片段(作为AST)转换为Java代码片段(作为AST)。后一种方案为您提供了更多的控制(如果Java代码片段是AST,您实际上可以对它们进行转换),并且如果操作正确,可以通过代码生成工具进行检查


我们将是一个很好的基础。DMS为构建翻译工具提供了一个生态系统,包括健壮的解析器机制、符号表支持、面向表面语法模式的翻译规则,并将通用SQL(SQL 2011,标准)作为一个可用的、经过测试的解析器,以及Java,用于代码生成方面。

,看起来promising@Sean,我不确定我是否正确理解了ANTLR,但那是用于字符串的解析和语法检查,对吗?但我想要的是“DSL内化”。输出应该是一组接口,使其看起来好像SQL可以用Java本机编写(jOOQ现在已经做了)。我将更新这个问题。是的,但是您可以使用SQL语法创建Java类alsoSweet,谢谢您的提示!这实际上可能是对我的问题的最好答案,尽管我开始觉得对于一个小型开源库来说,这是一个相当过分的做法。但它看起来确实很有趣!谢谢你的回答!