> I think having the possibility of applying simple business validation of
> values when going into and from jOOQ directly at the mapping stage is a nice
> feature and removes the burden of external checks everytime a particular
> column is accessed. It is also simple to understand and does not require an
> additional dedicated API.

I think the current design isn't entirely orthogonal to what you are
now planning to think about. Anyway, using converters/mappers also as
validators is up to you. jOOQ will be oblivious of these things and
propagate your custom exception wrapped in an
org.jooq.exception.DataTypeException...

Yet, I have to re-explain about jOOQ applying some magic with a
central registry. See, <T> is bound to MyEnum in generated code. This
means that all of jOOQ's API is concerned by this. My HSQLDB test case
mentions predicates:

T_MAPPED_TYPES.DEFAULT_ENUM_NAME.equal(StringEnum.A)

The equal method will use the bound of <T> to enforce compile-time
safety. It is not possible to compare such a field with String! This
is a simple example. You may argue that org.jooq.Comparison knows both
sides of the predicate, so it can "guess" the converter from the
left-hand side. But that's not a good move, as the bound of <T> can
propagate further, using expressions like:

nvl(T_MAPPED_TYPES.DEFAULT_ENUM_NAME, StringEnum.A).equal(StringEnum.A)

The Field.equal(T) method is a convenience method for
Field.equal(Field<T>) using Factory.val(T) internally to create the
bind value for the predicate's right-hand side. This is why I chose
the original example of selecting 'VAL' from dual. It *must* be
possible to create bind values for all custom types that are somehow
registered as user types in some type conversion. In jOOQ's internals,
the <T> type of all QueryParts *must* remain consistent. It will only
be converted when inlined in SQL or when bound as bind variable. Hence
there *must* be a "magic" central registry, because it is
programmatically impossible for jOOQ to resolve all custom types
through complex SQL expressions by accessing *some* involved field
which has a specific converter attached...

Solution for your use case, you have several options:
--------------------------------------------------------

1) Don't use jooq-codegen, to generate your user-defined date types as
bounds for <T>. You can still use jOOQ's converters and convert to
java.sql.Timestamp *before* binding, and convert to your type again in
the new Record <T, U> U getValue(Field<T> field, Converter<? super T,
U> converter) method *after* fetching (i.e. jOOQ will never see your
custom type)

2) Move that sort of mapping entirely out of jOOQ. Maybe there's a
much simpler solution, as this all feels super-overengineered (at
least to me...)

3) Create dedicated types for your 3 date types: NullableDate
(null=null), LowerBoundDate (null=-inf), UpperBoundDate (null=+inf).
All three extending some base type. Then you can use a dedicated
converter for each date type. This additional type-safety might even
be useful for your application's Java code

4) (probably not practicable): You can always use a non-null value to
represent -inf/+inf. Just like Integer.MIN_VALUE and Integer.MAX_VALUE
could be used for that.

Reply via email to