Reputation: 169
I'm wondering how to leverage the new range types you can find in the latests versions of PostgresSQL.
I can find the jOOQ documentation on how to manage normal column with custom Java types, however I cannot find how to use custom-type column into custom Java Types. Is there a documentation, or a piece of existing code in jOOQ I can look into and try to contribute?
Upvotes: 5
Views: 926
Reputation: 221145
Range type support has been added to jOOQ 3.17.0, see #2968. For this, the jooq-postgres-extensions
module needs to be present on the classpath of the code generator and the runtime, to offer the appropriate bindings and array bindings.
Historic answer for older jOOQ versions:
However, you can implement support yourself using jOOQ 3.5 custom data type Binding
. What you essentially need is a Converter
like this:
public class Int4RangeConverter implements Converter<Object, Range<Integer>> {
private static final Pattern PATTERN = Pattern.compile("\\[(.*?),(.*?)\\)");
@Override
public Range<Integer> from(Object t) {
if (t == null)
return null;
Matcher m = PATTERN.matcher("" + t);
if (m.find())
return Tuple.range(
Integer.valueOf(m.group(1)),
Integer.valueOf(m.group(2)));
throw new IllegalArgumentException("Unsupported range : " + t);
}
@Override
public Object to(Range<Integer> u) {
return u == null ? null : "[" + u.v1 + "," + u.v2 + ")";
}
@Override
public Class<Object> fromType() {
return Object.class;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Class<Range<Integer>> toType() {
return (Class) Range.class;
}
}
The above converter uses jOOL's org.jooq.lambda.tuple.Range
type, but you can implement your own, or just use int[]
if you prefer that.
The Converter
can now be used in a Binding
like this:
public class PostgresInt4RangeBinding implements Binding<Object, Range<Integer>> {
@Override
public Converter<Object, Range<Integer>> converter() {
return new Int4RangeConverter();
}
@Override
public void sql(BindingSQLContext<Range<Integer>> ctx) throws SQLException {
ctx.render()
.visit(DSL.val(ctx.convert(converter()).value()))
.sql("::int4range");
}
// ...
}
More information about how to use custom data type bindings can be found in the manual. Ideally, you will apply this binding to generated code.
Range
type in SQL:Using it is simple. Insertion:
DSL.using(configuration)
.insertInto(T_EXOTIC_TYPES)
.columns(T_EXOTIC_TYPES.ID, T_EXOTIC_TYPES.RANGE_INT4)
.values(1, range(1, 5))
.execute();
Selection:
assertEquals(range(1, 5), DSL.using(configuration).fetchValue(
select(T_EXOTIC_TYPES.RANGE_INT4)
.from(T_EXOTIC_TYPES)
.where(rangeOverlaps(T_EXOTIC_TYPES.RANGE_INT4, range(0, 2))))
);
With the following definition of rangeOverlaps()
:
private static <T extends Comparable<T>> Condition rangeOverlaps(
Field<Range<T>> f1, Range<T> f2) {
return DSL.condition("range_overlaps({0}, {1})", f1, val(f2, f1.getDataType()));
}
Upvotes: 5