[jOOQ/jOOQ#10438] Support lazy DataType lookups in DSL.val() and

DSL.inline()

This includes [jOOQ/jOOQ#9492] Deprecate static data type registry
lookups for user defined data types
This commit is contained in:
Lukas Eder 2020-07-23 16:44:52 +02:00
parent 84e8acc750
commit af2f5bc935
4 changed files with 238 additions and 8 deletions

View File

@ -78,6 +78,10 @@ final class ConvertedDataType<T, U> extends AbstractDataType<U> {
this.delegate = delegate;
this.binding = binding;
// [#9492] For backwards compatibility reasons, a legacy type registers
// itself in the static type registry
new LegacyConvertedDataType<>(delegate, binding);
}
@Override
@ -270,4 +274,5 @@ final class ConvertedDataType<T, U> extends AbstractDataType<U> {
delegate.get(ctx.convert(suffix));
}
}
}
}

View File

@ -23791,7 +23791,7 @@ public class DSL {
@Support
public static <T> Param<T> val(T value) {
Class type = value == null ? Object.class : value.getClass();
return val(value, DefaultDataType.getDataType(DEFAULT, type, new DataTypeProxy((AbstractDataType) SQLDataType.OTHER)));
return val(value, getDataType0(type));
}
/**
@ -25925,7 +25925,7 @@ public class DSL {
protected static <T> Field<T> nullSafe(Field<T> field, DataType<?> type) {
return field == null
? (Field<T>) val((T) null, type)
: field instanceof Val && field.getDataType() instanceof DataTypeProxy
: field instanceof Val
? (Field<T>) ((Val<T>) field).convertTo(type)
: field;
}
@ -26102,6 +26102,29 @@ public class DSL {
return DefaultDataType.getDataType(SQLDialect.DEFAULT, type);
}
/**
* [#9492] [#10438] Get a static data type for a class.
* <p>
* This will:
* <ul>
* <li>Get a built-in data type, if available</li>
* <li>Get a DataTypeProxy wrapped LegacyConvertedDataType, if available
* (#9492)</li>
* <li>Get a DataTypeProxy, otherwise, for lazy data type lookups
* (#10438)</li>
* </ul>
*/
static <T> DataType<T> getDataType0(Class<T> type) {
DataType t = DefaultDataType.getDataType(DEFAULT, type, (DataType) SQLDataType.OTHER);
if (t instanceof LegacyConvertedDataType)
return new DataTypeProxy((AbstractDataType) t);
else if (t != SQLDataType.OTHER)
return t;
else
return DefaultDataType.getDataType(DEFAULT, type, new DataTypeProxy((AbstractDataType) SQLDataType.OTHER));
}
private static final DSLContext dsl() {
return using(new DefaultConfiguration());
}

View File

@ -0,0 +1,185 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import java.sql.SQLException;
import org.jooq.Binding;
import org.jooq.BindingGetResultSetContext;
import org.jooq.BindingGetSQLInputContext;
import org.jooq.BindingGetStatementContext;
import org.jooq.BindingRegisterContext;
import org.jooq.BindingSQLContext;
import org.jooq.BindingSetSQLOutputContext;
import org.jooq.BindingSetStatementContext;
import org.jooq.Configuration;
import org.jooq.Converter;
import org.jooq.Converters;
import org.jooq.DataType;
import org.jooq.Field;
/**
* @author Lukas Eder
* @deprecated - 3.14.0 - [#9492] [#10312] - A compatibility implementation for
* converted data types, which registers itself in the static type
* registry for legacy reasons.
*/
@Deprecated
final class LegacyConvertedDataType<T, U> extends DefaultDataType<U> {
/**
* Generated UID
*/
private static final long serialVersionUID = -2321926692580974126L;
private final DataType<T> delegate;
@SuppressWarnings("unchecked")
LegacyConvertedDataType(AbstractDataType<T> delegate, Binding<? super T, U> binding) {
super(
null,
binding.converter().toType(),
binding,
delegate.getQualifiedName(),
delegate.getTypeName(),
delegate.getCastTypeName(),
delegate.precisionDefined() ? delegate.precision() : null,
delegate.scaleDefined() ? delegate.scale() : null,
delegate.lengthDefined() ? delegate.length() : null,
delegate.nullability(),
(Field<U>) delegate.defaultValue()
);
this.delegate = delegate;
}
@Override
public int getSQLType() {
return delegate.getSQLType();
}
@Override
public String getTypeName(Configuration configuration) {
return delegate.getTypeName(configuration);
}
@Override
public String getCastTypeName(Configuration configuration) {
return delegate.getCastTypeName(configuration);
}
@SuppressWarnings("unchecked")
@Override
public U convert(Object object) {
if (getConverter().toType().isInstance(object))
return (U) object;
// [#3200] Try to convert arbitrary objects to T
else
return ((Converter<T, U>) getConverter()).from(delegate.convert(object));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public <X> DataType<X> asConvertedDataType(Converter<? super U, X> converter) {
return super.asConvertedDataType(new ChainedConverterBinding(getBinding(), converter));
}
/**
* A binding that chains a new converter to an existing binding / converter.
*
* @author Lukas Eder
*/
private static class ChainedConverterBinding<T, U1, U2> implements Binding<T, U2> {
/**
* Generated UID
*/
private static final long serialVersionUID = -5120352678786683423L;
private final Binding<T, U1> delegate;
private final Converter<U1, U2> suffix;
private final Converter<T, U2> chained;
ChainedConverterBinding(Binding<T, U1> delegate, Converter<U1, U2> converter) {
this.delegate = delegate;
this.suffix = converter;
this.chained = Converters.of(delegate.converter(), converter);
}
@Override
public Converter<T, U2> converter() {
return chained;
}
@Override
public void sql(BindingSQLContext<U2> ctx) throws SQLException {
delegate.sql(ctx.convert(suffix));
}
@Override
public void register(BindingRegisterContext<U2> ctx) throws SQLException {
delegate.register(ctx.convert(suffix));
}
@Override
public void set(BindingSetStatementContext<U2> ctx) throws SQLException {
delegate.set(ctx.convert(suffix));
}
@Override
public void set(BindingSetSQLOutputContext<U2> ctx) throws SQLException {
delegate.set(ctx.convert(suffix));
}
@Override
public void get(BindingGetResultSetContext<U2> ctx) throws SQLException {
delegate.get(ctx.convert(suffix));
}
@Override
public void get(BindingGetStatementContext<U2> ctx) throws SQLException {
delegate.get(ctx.convert(suffix));
}
@Override
public void get(BindingGetSQLInputContext<U2> ctx) throws SQLException {
delegate.get(ctx.convert(suffix));
}
}
}

View File

@ -44,6 +44,8 @@ import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.concurrent.ConcurrentHashMap;
import org.jooq.Context;
import org.jooq.DataType;
@ -51,6 +53,7 @@ import org.jooq.EmbeddableRecord;
import org.jooq.RenderContext;
import org.jooq.conf.ParamType;
import org.jooq.exception.DataAccessException;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
/**
@ -58,7 +61,9 @@ import org.jooq.tools.StringUtils;
*/
final class Val<T> extends AbstractParam<T> {
private static final long serialVersionUID = 6807729087019209084L;
private static final long serialVersionUID = 6807729087019209084L;
private static final JooqLogger log = JooqLogger.getLogger(Val.class);
private static final ConcurrentHashMap<Class<?>, Object> legacyWarnings = new ConcurrentHashMap<>();
Val(T value, DataType<T> type) {
super(value, type);
@ -75,10 +80,22 @@ final class Val<T> extends AbstractParam<T> {
/**
* [#10438] Convert this bind value to a new type.
*/
<U> Val<U> convertTo(DataType<U> type) {
Val<U> w = new Val<>(type.convert(getValue()), type, getParamName());
w.setInline(isInline());
return w;
@SuppressWarnings({ "rawtypes", "unchecked" })
final <U> Val<U> convertTo(DataType<U> type) {
if (getDataType() instanceof DataTypeProxy) {
if (((DataTypeProxy<?>) getDataType()).type instanceof LegacyConvertedDataType && type == SQLDataType.OTHER) {
type = (DataType) ((DataTypeProxy<?>) getDataType()).type;
if (legacyWarnings.size() < 8 && legacyWarnings.put(type.getType(), "") == null)
log.warn("Deprecation", "User-defined, converted data type " + type.getType() + " was registered statically, which will be unsupported in the future, see https://github.com/jOOQ/jOOQ/issues/9492. Please use explicit data types in generated code, or e.g. with DSL.val(Object, DataType), or DSL.inline(Object, DataType).", new SQLWarning("Static type registry usage"));
}
Val<U> w = new Val<>(type.convert(getValue()), type, getParamName());
w.setInline(isInline());
return w;
}
else
return (Val) this;
}
@Override