I have recently posted an article about how to generally design a fluent API in Java. By fluent API, I don’t mean simple constructs such as
new Builder().withSomething(x) .withSomethingElse(y) .withSomething(z) .withAnotherThing(xx);
The above is just simple method-chaining, without any sophisticated formal language definition. Most often in method-chaining contexts, the order of method calls is irrelevant to the API and may be chosen freely (i.e. you can call “withAnotherThing()” first, then “withSomethingElse()”. Instead, I mean a full-fledged domain specific language, formally defined using BNF notation (or anything equivalent).
jOOQ’s fluent API
Today, I’d like to give some insight about how jOOQ can be formally expressed as a true domain specific language using BNF notation. jOOQ in itself has actually evolved to become a SQL dialect of its own. It knows most of the standard DML SQL statements and clauses, as well as many vendor-specific ones. Let’s have a look at jOOQ 2.0’s understanding of a SELECT statement:
SELECT ::= ( ( 'select' | 'selectDistinct' ) FIELD* | 'selectOne' | 'selectZero' | 'selectCount' ) ( 'select' FIELD* )* ( 'hint' SQL )* ( 'from' ( TABLE+ | SQL ) ( ( 'join' | 'leftOuterJoin' | 'rightOuterJoin' | 'fullOuterJoin' ) ( TABLE | SQL ) ( 'on' ( CONDITION+ | SQL ) MORE_CONDITIONS? | 'using' FIELD+ ) | ( 'crossJoin' | 'naturalJoin' | 'naturalLeftOuterJoin' | 'naturalRightOuterJoin' ) ( TABLE | SQL ) )* )? ( ( 'where' ( CONDITION+ | SQL ) | ( 'whereExists' | 'whereNotExists' ) SELECT ) MORE_CONDITIONS? )? ( ( 'connectBy' | 'connectByNoCycle' ) ( CONDITION | SQL ) ( 'and' ( CONDITION | SQL ) )* ( 'startWith' ( CONDITION | SQL ) )? )? ( 'groupBy' GROUP_FIELD+ )? ( 'having' ( CONDITION+ | SQL ) MORE_CONDITIONS? )? ( 'orderBy' ( FIELD+ | SORT_FIELD+ | INT+ ) )? ( ( 'limit' INT ( 'offset' INT | INT )? ) | ( ( 'forUpdate' ( 'of' ( FIELD+ | TABLE+ ) )? ( 'wait' INT | 'noWait' | 'skipLocked' )? ) | 'forShare' ) )? ( ( 'union' | 'unionAll' | 'except' | 'intersect' ) SELECT )*
Or in the much more readable graphical representation:
Unlike simpler querying API’s such as the JPA Criteria Query API or QueryDSL, for example, jOOQ really emphasises on SQL syntax correctness. In that way, it is impossible to form complex SQL queries in a nonsensical way such as providing “JOIN” clauses without previously declaring at least one table source, or providing “ON” clauses without previously defining a “JOIN” clause, or providing an “ON” clause on a “CROSS JOIN”, etc, etc.
The full jOOQ BNF representation can be seen here:
- http://www.jooq.org/sql-as-seen-by-jooq.svg (graphical representation)
- http://www.jooq.org/sql-as-seen-by-jooq.txt (text representation)
If you compare this BNF and its output (jOOQ) to what I’ve written in my previous blog post about fluent API design, you can see that there is potential for complete formalisation of this process. In principle, any arbitrary BNF can be formally transformed into a set of Java interfaces modelling your domain specific language directly in Java. I will soon create a prototype source code generator for this, so stay tuned!