This is experimental functionality, and as such subject to change. Use at your own risk!
Traversal
Applies to ❌ Open Source Edition ✅ Express Edition ✅ Professional Edition ✅ Enterprise Edition
While the accessor methods from the model API allow for traversing the expression tree manually, a more generic way to traverse the expression tree is using the org.jooq.Traverser
API, which can traverse a org.jooq.QueryPart
in a similar fashion as a java.util.stream.Collector
can iterate a java.util.stream.Stream
, collecting and aggregating data about the expression tree. A Traverser consists of this API
:
public interface Traverser<A, R> { /** * A supplier for a temporary data structure to accumulate {@link QueryPart} * objects into during traversal. */ Supplier<A> supplier(); /** * An optional traversal abort condition to short circuit traversal e.g. * when the searched object has been found. */ Predicate<A> abort(); /** * An optional recursion condition to prevent entering a specific * {@link QueryPart}, e.g. when it is undesired to enter any subqueries. */ Predicate<QueryPart> recurse(); /** * An optional recursion condition to prevent entering a specific * {@link QueryPart}'s children, e.g. when it is desired to traverse only * into certain operators. */ Predicate<QueryPart> recurseChildren(); /** * A callback that is invoked before recursing into a subtree. */ BiFunction<A, QueryPart, A> before(); /** * A callback that is invoked after recursing into a subtree. */ BiFunction<A, QueryPart, A> after(); /** * An optional transformation function to turn the temporary data structure * supplied by {@link #supplier()} into the final data structure. */ Function<A, R> finisher(); }
Some elements are similar to that of a java.util.stream.Collector
, others are specific to tree traversal.
A simple illustration shows what can be done:
// Any ordinary QueryPart: Condition condition = BOOK.ID.eq(1); int count = condition.$traverse( // Supplier of the initial data structure: an int () -> 0, // Print all traversed QueryParts and increment the counter (r, q) -> { System.out.println("Part " + r + ": " + q); return r + 1; } ); System.out.println("Count : " + count);
The above will print:
Part 0: "BOOK"."ID" = 1 Part 1: "BOOK"."ID" Part 2: 1 Count : 3
Using the same traverser on a slightly more complex QueryPart
Condition condition = BOOK.ID.eq(1).or(BOOK.ID.eq(2)); // ...
Part 0: ("BOOK"."ID" = 1 or "BOOK"."ID" = 2) Part 1: "BOOK"."ID" = 1 Part 2: "BOOK"."ID" Part 3: 1 Part 4: "BOOK"."ID" = 2 Part 5: "BOOK"."ID" Part 6: 2 Count : 7
Re-using your JDK collectors
Any Collector
can be turned into a Traverser
using Traversers.collecting(Collector)
. For example, if you want to count all QueryPart
items in an expression, instead of the above hand-written traverser, just use the JDK Collectors.counting()
:
// Contains 3 query parts long count1 = BOOK.ID.eq(1) .$traverse(Traversers.collecting(Collectors.counting()); // Contains 7 query parts long count2 = BOOK.ID.eq(1).or(BOOK.ID.eq(2)) .$traverse(Traversers.collecting(Collectors.counting());
Limitations
Just like model API replacement, traversers cannot traverse into "opaque" org.jooq.QueryPart
instances, including custom QueryParts or plain SQL templates. See also features requiring code generation for more details.
Feedback
Do you have any feedback about this page? We'd love to hear it!