> 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.
