From af2f5bc93504a1f3bf7216e10e3a9aab2635e2ad Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 23 Jul 2020 16:44:52 +0200 Subject: [PATCH] [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 --- .../java/org/jooq/impl/ConvertedDataType.java | 7 +- jOOQ/src/main/java/org/jooq/impl/DSL.java | 27 ++- .../jooq/impl/LegacyConvertedDataType.java | 185 ++++++++++++++++++ jOOQ/src/main/java/org/jooq/impl/Val.java | 27 ++- 4 files changed, 238 insertions(+), 8 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java diff --git a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java index 11ece819da..24d12403f9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java @@ -78,6 +78,10 @@ final class ConvertedDataType extends AbstractDataType { 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 extends AbstractDataType { delegate.get(ctx.convert(suffix)); } } -} \ No newline at end of file +} + diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 8b8a07ee53..8fa7168333 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -23791,7 +23791,7 @@ public class DSL { @Support public static Param 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 Field nullSafe(Field field, DataType type) { return field == null ? (Field) val((T) null, type) - : field instanceof Val && field.getDataType() instanceof DataTypeProxy + : field instanceof Val ? (Field) ((Val) 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. + *

+ * This will: + *

    + *
  • Get a built-in data type, if available
  • + *
  • Get a DataTypeProxy wrapped LegacyConvertedDataType, if available + * (#9492)
  • + *
  • Get a DataTypeProxy, otherwise, for lazy data type lookups + * (#10438)
  • + *
+ */ + static DataType getDataType0(Class 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()); } diff --git a/jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java b/jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java new file mode 100644 index 0000000000..48ec4b1959 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/LegacyConvertedDataType.java @@ -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 extends DefaultDataType { + + /** + * Generated UID + */ + private static final long serialVersionUID = -2321926692580974126L; + + private final DataType delegate; + + @SuppressWarnings("unchecked") + LegacyConvertedDataType(AbstractDataType delegate, Binding 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) 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) getConverter()).from(delegate.convert(object)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public DataType asConvertedDataType(Converter 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 implements Binding { + + /** + * Generated UID + */ + private static final long serialVersionUID = -5120352678786683423L; + + private final Binding delegate; + private final Converter suffix; + private final Converter chained; + + ChainedConverterBinding(Binding delegate, Converter converter) { + this.delegate = delegate; + this.suffix = converter; + this.chained = Converters.of(delegate.converter(), converter); + } + + @Override + public Converter converter() { + return chained; + } + + @Override + public void sql(BindingSQLContext ctx) throws SQLException { + delegate.sql(ctx.convert(suffix)); + } + + @Override + public void register(BindingRegisterContext ctx) throws SQLException { + delegate.register(ctx.convert(suffix)); + } + + @Override + public void set(BindingSetStatementContext ctx) throws SQLException { + delegate.set(ctx.convert(suffix)); + } + + @Override + public void set(BindingSetSQLOutputContext ctx) throws SQLException { + delegate.set(ctx.convert(suffix)); + } + + @Override + public void get(BindingGetResultSetContext ctx) throws SQLException { + delegate.get(ctx.convert(suffix)); + } + + @Override + public void get(BindingGetStatementContext ctx) throws SQLException { + delegate.get(ctx.convert(suffix)); + } + + @Override + public void get(BindingGetSQLInputContext ctx) throws SQLException { + delegate.get(ctx.convert(suffix)); + } + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/Val.java b/jOOQ/src/main/java/org/jooq/impl/Val.java index 5a9f8baa06..802ff93def 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Val.java +++ b/jOOQ/src/main/java/org/jooq/impl/Val.java @@ -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 extends AbstractParam { - 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, Object> legacyWarnings = new ConcurrentHashMap<>(); Val(T value, DataType type) { super(value, type); @@ -75,10 +80,22 @@ final class Val extends AbstractParam { /** * [#10438] Convert this bind value to a new type. */ - Val convertTo(DataType type) { - Val w = new Val<>(type.convert(getValue()), type, getParamName()); - w.setInline(isInline()); - return w; + @SuppressWarnings({ "rawtypes", "unchecked" }) + final Val convertTo(DataType 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 w = new Val<>(type.convert(getValue()), type, getParamName()); + w.setInline(isInline()); + return w; + } + else + return (Val) this; } @Override