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?

有帮助吗?

解决方案

jOOQ 3.17 range type support

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:

Custom Converter:

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.

Custom Binding

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.

Using the 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()));
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top