Hello all,
One thing I felt missing this week is an easy way to create a custom
derived table.
When writing a complex (sub-)query, I sometimes like to package it in its
own class, together with its (usually aliases) fields.
Here a very simple example:
// Packaged query:
public class MyQuery {
public final Table<...> QUERY;
public final Field<Integer> FIELD;
public MyQuery(String alias) {
// The query
this.QUERY =
select(SOME_TABLE.SOME_FIELD)
.from(SOME_TABLE)
.asTable(alias);
// The fields of the query, aliased accordingly
this.FIELD = QUERY.field(SOME_FIELD);
}
}
// Usage:
MyQuery q1 = new MyQuery("hello");
MyQuery q2 = new MyQuery("world");
select(q1.FIELD, q2.FIELD).from(q1.QUERY.join(q2.QUERY).on(...));
The idea being that I can alias my packaged query very easily, almost as if
it was a generated table.
Not so useful here, but worth the trouble when the query has a lot of
fields.
That was to explain the idea. Now the real thing:
Since it is *almost *like a generated table, why not make it *exactly *like
a generated table? That is make MyQuery into an actual Table rather than
having a Table field.
Here is what I would find very cool:
public class MyQuery extends SomeKindOfTableImpl<...> {
public final static MyQuery MY_QUERY = new MyQuery();
public final Field<Integer> FIELD = createField("field", ...);
public MyQuery() {
setTheQuery(
select(anyExpression.as(FIELD))
...;
);
}
}
// And then use it as any other table:
select(MY_QUERY.FIELD).from(MY_QUERY);
// Or aliased:
MyQuery q = MY_QUERY.as("yop");
select(q.FIELD).from(q);
And here is a very very ugly try at a SomeKindOfTableImpl implementation:
public abstract class DerivedTableImpl<R extends Record> extends TableImpl<R
> {
/**
* Default constructor.<br>
* The underlying query must then be set by subclasses through {@code
query()}.
*/
protected DerivedTableImpl(String name) {
super(name);
}
/**
* Copy constructor, to be used when implementing {@code as()} in
subclasses.<br>
* The underlying query is copied from the given table.
*/
protected DerivedTableImpl(String name, DerivedTableImpl<R> other) {
super(name);
setWrapped(this, getWrapped(other));
}
/**
* Sets the underlying query.<br>
* Should be called by subclasses in their (not copy) constructor.
*/
protected DerivedTableImpl<R> query(Select<? extends R> query) {
setWrapped(this, createDerivedTable(query));
return this;
}
// Force subclasses to covariantly override this method
/** {@inheritDoc} **/
@Override
public abstract DerivedTableImpl<R> as(String as);
//////// BLACK MAGIC
/**
* Set the internal alias of a {@code TableImpl}, even though field and
class are final and package-private.<br>
* Allow to control the {@code wrapInParentheses} flag, and to set the
alias after the constructor call.
*/
private static <R extends Record> void setWrapped(TableImpl<R> table,
Table<R> wrapped) {
Object alias = wrapped == null ? null : on("org.jooq.impl.Alias").
create(wrapped, table.getName(), true).get();
on(table).set("alias", alias);
}
/**
* Get the table wrapped in the internal alias of a {@code TableImpl}.
*/
private static <R extends Record> Table<R> getWrapped(TableImpl<R> table
) {
Object alias = on(table).get("alias");
return alias == null ? null : on(alias).call("wrapped").get();
}
/**
* Create a {@code DerivedTable} instance, even though the class is
package-private.<br>
* Allow to get a clean {@code DerivedTable} without aliasing (unlike
{@code Select.asTable()}).
*/
private static <R extends Record> Table<R> createDerivedTable(Select<?
extends R> query) {
return Reflect.on("org.jooq.impl.DerivedTable").create(query).get();
}
}
Not intended to be used in real-life, but it seems to work. Just to show
that all necessary code is already there, it's just not accessible.
For those interested, there is a way to do it without black-magic (create a
Table
implementation delegating to query.asTable(), tweak accept() to only visit
the original query and add extra parenthesis, and use an instance of this
as the aliased param of TableImpl constructor). But it is quite cumbersome.
Well! I may be the only one needing such a feature, but I found it very
handy when packaging custom queries together with jOOQ generated classes in
one single lib.
Cheers,
Thomas
--
You received this message because you are subscribed to the Google Groups "jOOQ
User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.