[jOOQ/jOOQ#13961] Add ScopedConverter, a Converter subtype that receives a ConverterScope in from() and to() methods

See also [jOOQ/jOOQ#13801]
This commit is contained in:
Lukas Eder 2022-09-06 09:21:27 +02:00
parent d2850a4695
commit 1bf4199b32
50 changed files with 784 additions and 283 deletions

View File

@ -39,39 +39,39 @@ package org.jooq;
import java.lang.reflect.Array;
import org.jooq.impl.AbstractConverter;
import org.jooq.impl.AbstractScopedConverter;
/**
* @author Lukas Eder
*/
@SuppressWarnings("unchecked")
final class ArrayComponentConverter<T, U> extends AbstractConverter<T, U> {
final class ArrayComponentConverter<T, U> extends AbstractScopedConverter<T, U> {
final Converter<T[], U[]> converter;
final ScopedConverter<T[], U[]> converter;
public ArrayComponentConverter(Converter<T[], U[]> converter) {
public ArrayComponentConverter(ScopedConverter<T[], U[]> converter) {
super((Class<T>) converter.fromType().getComponentType(), (Class<U>) converter.toType().getComponentType());
this.converter = converter;
}
@Override
public final U from(T t) {
public final U from(T t, ConverterScope scope) {
if (t == null)
return null;
T[] a = (T[]) Array.newInstance(fromType(), 1);
a[0] = t;
return converter.from(a)[0];
return converter.from(a, scope)[0];
}
@Override
public final T to(U u) {
public final T to(U u, ConverterScope scope) {
if (u == null)
return null;
U[] a = (U[]) Array.newInstance(fromType(), 1);
a[0] = u;
return converter.to(a)[0];
return converter.to(a, scope)[0];
}
}

View File

@ -39,7 +39,7 @@ package org.jooq;
import static org.jooq.impl.Internal.arrayType;
import org.jooq.impl.AbstractConverter;
import org.jooq.impl.AbstractScopedConverter;
import org.jooq.tools.Convert;
/**
@ -48,12 +48,12 @@ import org.jooq.tools.Convert;
*
* @author Lukas Eder
*/
final class ArrayConverter<T, U> extends AbstractConverter<T[], U[]> {
final class ArrayConverter<T, U> extends AbstractScopedConverter<T[], U[]> {
final Converter<T, U> converter;
final Converter<U, T> inverse;
final ScopedConverter<T, U> converter;
final ScopedConverter<U, T> inverse;
public ArrayConverter(Converter<T, U> converter) {
public ArrayConverter(ScopedConverter<T, U> converter) {
super(arrayType(converter.fromType()), arrayType(converter.toType()));
this.converter = converter;
@ -61,12 +61,12 @@ final class ArrayConverter<T, U> extends AbstractConverter<T[], U[]> {
}
@Override
public final U[] from(T[] t) {
public final U[] from(T[] t, ConverterScope scope) {
return Convert.convertArray(t, converter);
}
@Override
public final T[] to(U[] t) {
public final T[] to(U[] t, ConverterScope scope) {
return Convert.convertArray(t, inverse);
}
}

View File

@ -98,4 +98,9 @@ import java.sql.SQLOutput;
*/
public interface BindingScope extends Scope {
/**
* Get the {@link ConverterScope} from the context of this
* {@link BindingScope}.
*/
ConverterScope converterScope();
}

View File

@ -45,6 +45,7 @@ import java.util.function.Function;
import org.jooq.exception.DataTypeException;
import org.jooq.impl.AbstractConverter;
import org.jooq.impl.AbstractScopedConverter;
import org.jooq.impl.SQLDataType;
import org.jetbrains.annotations.NotNull;
@ -171,15 +172,15 @@ public interface Converter<T, U> extends Serializable {
Function<? super T, ? extends U> from,
Function<? super U, ? extends T> to
) {
return new AbstractConverter<T, U>(fromType, toType) {
return new AbstractScopedConverter<T, U>(fromType, toType) {
@Override
public final U from(T t) {
public final U from(T t, ConverterScope scope) {
return from.apply(t);
}
@Override
public final T to(U u) {
public final T to(U u, ConverterScope scope) {
return to.apply(u);
}
};

View File

@ -0,0 +1,48 @@
/*
* 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;
/**
* A {@link Scope} that models the life cycle of a data type conversion call to
* {@link Converter#from(Object)} or {@link Converter#to(Object)}.
* <p>
* Implementations
*/
public interface ConverterScope extends Scope {
}

View File

@ -38,10 +38,12 @@
package org.jooq;
import java.io.Serializable;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.jooq.exception.DataTypeException;
import org.jooq.impl.AbstractConverter;
import org.jooq.impl.AbstractScopedConverter;
import org.jooq.impl.IdentityConverter;
import org.jooq.impl.SQLDataType;
@ -57,15 +59,15 @@ import org.jetbrains.annotations.NotNull;
* @author Lukas Eder
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Converters<T, U> extends AbstractConverter<T, U> {
public final class Converters<T, U> extends AbstractScopedConverter<T, U> {
final Converter[] chain;
final ScopedConverter[] chain;
/**
* Create an identity converter.
*/
@NotNull
public static <T> Converter<T, T> identity(final Class<T> type) {
public static <T> ScopedConverter<T, T> identity(final Class<T> type) {
return new IdentityConverter<T>(type);
}
@ -77,7 +79,7 @@ public class Converters<T, U> extends AbstractConverter<T, U> {
*/
@Deprecated(forRemoval = true, since = "3.14")
@NotNull
public static <T, U> Converter<T, U> of() {
public static <T, U> ScopedConverter<T, U> of() {
return new Converters();
}
@ -89,44 +91,63 @@ public class Converters<T, U> extends AbstractConverter<T, U> {
*/
@Deprecated(forRemoval = true, since = "3.14")
@NotNull
public static <T, U> Converter<T, U> of(Converter<T, U> converter) {
return new Converters(converter);
public static <T, U> ScopedConverter<T, U> of(Converter<T, U> converter) {
return new Converters(ScopedConverter.scoped(converter));
}
/**
* Chain two converters.
*/
@NotNull
public static <T, X1, U> Converter<T, U> of(Converter<T, ? extends X1> c1, Converter<? super X1, U> c2) {
return new Converters(c1, c2);
public static <T, X1, U> ScopedConverter<T, U> of(Converter<T, ? extends X1> c1, Converter<? super X1, U> c2) {
return new Converters(
ScopedConverter.scoped(c1),
ScopedConverter.scoped(c2)
);
}
/**
* Chain three converters.
*/
@NotNull
public static <T, X1, X2, U> Converter<T, U> of(Converter<T, ? extends X1> c1, Converter<? super X1, ? extends X2> c2, Converter<? super X2, U> c3) {
return new Converters(c1, c2, c3);
public static <T, X1, X2, U> ScopedConverter<T, U> of(Converter<T, ? extends X1> c1, Converter<? super X1, ? extends X2> c2, Converter<? super X2, U> c3) {
return new Converters(
ScopedConverter.scoped(c1),
ScopedConverter.scoped(c2),
ScopedConverter.scoped(c3)
);
}
/**
* Chain four converters.
*/
@NotNull
public static <T, X1, X2, X3, U> Converter<T, U> of(Converter<T, ? extends X1> c1, Converter<? super X1, ? extends X2> c2, Converter<? super X2, ? extends X3> c3, Converter<? super X3, U> c4) {
return new Converters(c1, c2, c3, c4);
public static <T, X1, X2, X3, U> ScopedConverter<T, U> of(Converter<T, ? extends X1> c1, Converter<? super X1, ? extends X2> c2, Converter<? super X2, ? extends X3> c3, Converter<? super X3, U> c4) {
return new Converters(
ScopedConverter.scoped(c1),
ScopedConverter.scoped(c2),
ScopedConverter.scoped(c3),
ScopedConverter.scoped(c4)
);
}
/**
* Inverse a converter.
*/
public static <T, U> Converter<U, T> inverse(final Converter<T, U> converter) {
return inverse(ScopedConverter.scoped(converter));
}
// [#11099] Allow instanceof checks on IdentityConverter for performance reasons
if (converter instanceof IdentityConverter)
return (Converter<U, T>) converter;
/**
* Inverse a converter.
*/
public static <T, U> ScopedConverter<U, T> inverse(final ScopedConverter<T, U> converter) {
// [#11099] Allow instanceof checks on IdentityConverter for performance reasons
if (converter instanceof IdentityConverter)
return (ScopedConverter<U, T>) converter;
else
return Converter.of(converter.toType(), converter.fromType(), converter::to, converter::from);
return ScopedConverter.of(converter.toType(), converter.fromType(), converter::to, converter::from);
}
/**
@ -134,6 +155,14 @@ public class Converters<T, U> extends AbstractConverter<T, U> {
* the argument converter's types.
*/
public static <T, U> Converter<T[], U[]> forArrays(Converter<T, U> converter) {
return forArrays(ScopedConverter.scoped(converter));
}
/**
* Create a converter that can convert arrays with the component types being
* the argument converter's types.
*/
public static <T, U> ScopedConverter<T[], U[]> forArrays(ScopedConverter<T, U> converter) {
if (converter instanceof ArrayComponentConverter<T, U> a)
return a.converter;
else
@ -145,35 +174,42 @@ public class Converters<T, U> extends AbstractConverter<T, U> {
* converter, which converts array types.
*/
public static <T, U> Converter<T, U> forArrayComponents(Converter<T[], U[]> converter) {
return forArrayComponents(ScopedConverter.scoped(converter));
}
/**
* Create a converter that can convert component types based on the argument
* converter, which converts array types.
*/
public static <T, U> Converter<T, U> forArrayComponents(ScopedConverter<T[], U[]> converter) {
if (converter instanceof ArrayConverter<T, U> a)
return a.converter;
else
return new ArrayComponentConverter<>(converter);
}
Converters(Converter... chain) {
Converters(ScopedConverter... chain) {
super(chain[0].fromType(), chain[chain.length - 1].toType());
this.chain = chain;
}
@Override
public final U from(T t) {
public final U from(T t, ConverterScope scope) {
Object result = t;
for (int i = 0; i < chain.length; i++)
result = chain[i].from(result);
result = chain[i].from(result, scope);
return (U) result;
}
@Override
public final T to(U u) {
public final T to(U u, ConverterScope scope) {
Object result = u;
for (int i = chain.length - 1; i >= 0; i--)
result = chain[i].to(result);
result = chain[i].to(result, scope);
return (T) result;
}
@ -201,6 +237,12 @@ public class Converters<T, U> extends AbstractConverter<T, U> {
: f.apply(t) : t -> t == null ? null : f.apply(t);
}
static final <T, U> BiFunction<T, ConverterScope, U> nullable(BiFunction<? super T, ? super ConverterScope, ? extends U> f) {
return f instanceof Serializable
? (BiFunction<T, ConverterScope, U> & Serializable) (t, x) -> t == null ? null
: f.apply(t, x) : (t, x) -> t == null ? null : f.apply(t, x);
}
static final <T, U> Function<T, U> notImplemented() {
return t -> { throw new DataTypeException("Conversion function not implemented"); };
}

View File

@ -139,7 +139,7 @@ public interface DataType<T> extends Named {
* Get the converter associated with this data type.
*/
@NotNull
Converter<?, T> getConverter();
ScopedConverter<?, T> getConverter();
/**
* Retrieve the Java type associated with this data type.

View File

@ -71,6 +71,12 @@ import org.jetbrains.annotations.Nullable;
*/
public interface ExecuteContext extends Scope {
/**
* Get a {@link ConverterScope} for the scope of this
* {@link ExecuteContext}.
*/
ConverterScope converterScope();
/**
* The connection to be used in this execute context.
* <p>

View File

@ -44,6 +44,12 @@ import org.jetbrains.annotations.Nullable;
*/
public interface ExecuteScope extends Scope {
/**
* Get the {@link ConverterScope} from the context of this
* {@link ExecuteScope}.
*/
ConverterScope converterScope();
/**
* The {@link ExecuteContext} that created this scope.
*

View File

@ -0,0 +1,186 @@
/*
* 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;
import static org.jooq.Converters.nullable;
import static org.jooq.impl.Internal.converterScope;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.jooq.impl.AbstractConverter;
import org.jooq.impl.AbstractScopedConverter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A special type of {@link Converter} with alternative
* {@link #from(Object, ConverterScope)} and {@link #to(Object, ConverterScope)}
* methods.
* <p>
* This special converter type can be used wherever an ordinary
* {@link Converter} is used. jOOQ internal call sites will call the alternative
* {@link #from(Object, ConverterScope)} and {@link #to(Object, ConverterScope)}
* methods, instead of {@link #from(Object)} and {@link #to(Object)}, allowing
* for accessing global {@link Configuration#data()} content.
*/
public interface ScopedConverter<T, U> extends Converter<T, U> {
/**
* Construct a new converter from functions.
*
* @param <T> the database type.
* @param <U> the user type.
* @param fromType The database type.
* @param toType The user type.
* @param from A function converting from T to U when reading from the
* database.
* @param to A function converting from U to T when writing to the database.
* @return The converter.
* @see Converter
*/
@NotNull
static <T, U> ScopedConverter<T, U> of(
Class<T> fromType,
Class<U> toType,
BiFunction<? super T, ? super ConverterScope, ? extends U> from,
BiFunction<? super U, ? super ConverterScope, ? extends T> to
) {
return new AbstractScopedConverter<T, U>(fromType, toType) {
@Override
public final U from(T t, ConverterScope scope) {
return from.apply(t, scope);
}
@Override
public final T to(U u, ConverterScope scope) {
return to.apply(u, scope);
}
};
}
/**
* Construct a new converter from functions.
* <p>
* This works like {@link Converter#of(Class, Class, Function, Function)},
* except that both conversion {@link Function}s are decorated with a
* function that always returns <code>null</code> for <code>null</code>
* inputs.
* <p>
* Example:
* <p>
* <pre><code>
* Converter&lt;String, Integer&gt; converter =
* Converter.ofNullable(String.class, Integer.class, Integer::parseInt, Object::toString);
*
* // No exceptions thrown
* assertNull(converter.from(null));
* assertNull(converter.to(null));
* </code></pre>
*
* @param <T> the database type
* @param <U> the user type
* @param fromType The database type
* @param toType The user type
* @param from A function converting from T to U when reading from the
* database.
* @param to A function converting from U to T when writing to the database.
* @return The converter.
* @see Converter
*/
@NotNull
static <T, U> ScopedConverter<T, U> ofNullable(
Class<T> fromType,
Class<U> toType,
BiFunction<? super T, ? super ConverterScope, ? extends U> from,
BiFunction<? super U, ? super ConverterScope, ? extends T> to
) {
return of(fromType, toType, nullable(from), nullable(to));
}
/**
* Turn a {@link Converter} into a {@link ScopedConverter}.
*/
@NotNull
static <T, U> ScopedConverter<T, U> scoped(Converter<T, U> converter) {
if (converter instanceof ScopedConverter<T, U> s)
return s;
else
return new AbstractScopedConverter<T, U>(converter.fromType(), converter.toType()) {
@Override
public U from(T t, ConverterScope scope) {
return converter.from(t);
}
@Override
public T to(U u, ConverterScope scope) {
return converter.to(u);
}
};
}
/**
* Read and convert a database object to a user object.
*
* @param databaseObject The database object.
* @param scope The scope of this conversion.
* @return The user object.
*/
U from(T databaseObject, ConverterScope scope);
/**
* Convert and write a user object to a database object.
*
* @param userObject The user object.
* @param scope The scope of this conversion.
* @return The database object.
*/
T to(U userObject, ConverterScope scope);
@Override
default T to(U userObject) {
return to(userObject, converterScope());
}
@Override
default U from(T databaseObject) {
return from(databaseObject, converterScope());
}
}

View File

@ -62,7 +62,7 @@ public interface Typed<T> extends QueryPart {
* the generated object.
*/
@NotNull
Converter<?, T> getConverter();
ScopedConverter<?, T> getConverter();
/**
* The object's underlying {@link Binding}.

View File

@ -78,6 +78,7 @@ import org.jooq.Clause;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.ConverterScope;
import org.jooq.DSLContext;
import org.jooq.ExecuteContext;
import org.jooq.Field;
@ -235,6 +236,11 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
// ExecuteScope API
// ------------------------------------------------------------------------
@Override
public final ConverterScope converterScope() {
return ctx != null ? ctx.converterScope() : Tools.converterScope(configuration());
}
@Override
public final ExecuteContext executeContext() {
return ctx;

View File

@ -106,6 +106,7 @@ import org.jooq.Result;
import org.jooq.Row;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.ScopedConverter;
import org.jooq.Table;
import org.jooq.XML;
import org.jooq.impl.QOM.GenerationLocation;
@ -114,6 +115,8 @@ import org.jooq.impl.QOM.UEmpty;
import org.jooq.types.Interval;
import org.jooq.types.UNumber;
import org.jetbrains.annotations.NotNull;
// ...
/**
@ -563,8 +566,8 @@ implements
}
@Override
public final Converter<?, T> getConverter() {
return getBinding().converter();
public final ScopedConverter<?, T> getConverter() {
return (@NotNull ScopedConverter<?, T>) ScopedConverter.scoped(getBinding().converter());
}
@Override

View File

@ -37,6 +37,7 @@
*/
package org.jooq.impl;
import org.jooq.ConverterScope;
import org.jooq.ExecuteContext;
import org.jooq.ExecuteScope;
@ -55,6 +56,11 @@ abstract class AbstractExecuteScope extends AbstractScope implements ExecuteScop
this.ctx = ctx;
}
@Override
public final ConverterScope converterScope() {
return ctx.converterScope();
}
@Override
public ExecuteContext executeContext() {
return ctx;

View File

@ -40,9 +40,11 @@ package org.jooq.impl;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.jooq.ScopedConverter.scoped;
import static org.jooq.conf.SettingsTools.updatablePrimaryKeys;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.converterOrFail;
import static org.jooq.impl.Tools.converterScope;
import static org.jooq.impl.Tools.embeddedFields;
import static org.jooq.impl.Tools.indexFail;
import static org.jooq.impl.Tools.indexOrFail;
@ -102,7 +104,6 @@ import org.jooq.XMLFormat;
import org.jooq.exception.IOException;
import org.jooq.exception.InvalidResultException;
import org.jooq.exception.MappingException;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
import org.jetbrains.annotations.NotNull;
@ -335,12 +336,12 @@ abstract class AbstractRecord extends AbstractStore implements Record {
@Override
public final <U> U get(Field<?> field, Class<? extends U> type) {
Object t = get(field);
return (U) converterOrFail(this, t, (Class) field.getType(), type).from(t);
return (U) converterOrFail(this, t, (Class) field.getType(), type).from(t, converterScope(this));
}
@Override
public final <T, U> U get(Field<T> field, Converter<? super T, ? extends U> converter) {
return converter.from(get(field));
return scoped(converter).from(get(field), converterScope(this));
}
@Override
@ -351,7 +352,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
@Override
public final <U> U get(int index, Class<? extends U> type) {
Object t = get(index);
return (U) converterOrFail(this, t, (Class) field(safeIndex(index)).getType(), type).from(t);
return (U) converterOrFail(this, t, (Class) field(safeIndex(index)).getType(), type).from(t, converterScope(this));
}
@Override
@ -474,7 +475,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
@Override
public final <T, U> void set(Field<T> field, U value, Converter<? extends T, ? super U> converter) {
set(field, converter.to(value));
set(field, scoped(converter).to(value, converterScope(this)));
}
@Override

View File

@ -64,6 +64,7 @@ import org.jooq.Record;
import org.jooq.Row;
import org.jooq.Row1;
import org.jooq.Row2;
import org.jooq.ScopedConverter;
import org.jooq.SelectField;
// ...
import org.jooq.impl.QOM.UnmodifiableList;
@ -151,7 +152,7 @@ abstract class AbstractRow<R extends Record> extends AbstractQueryPart implement
}
@Override
public final Converter<?, R> getConverter() {
public final ScopedConverter<?, R> getConverter() {
return rf().getConverter();
}

View File

@ -0,0 +1,55 @@
/*
* 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 org.jooq.ScopedConverter;
/**
* @author Lukas Eder
*/
public abstract class AbstractScopedConverter<T, U> extends AbstractConverter<T, U> implements ScopedConverter<T, U> {
public AbstractScopedConverter(Class<T> fromType, Class<U> toType) {
super(fromType, toType);
}
@Override
public String toString() {
return "ScopedConverter [ " + fromType().getName() + " -> " + toType().getName() + " ]";
}
}

View File

@ -107,6 +107,7 @@ import org.jooq.Row;
import org.jooq.RowId;
import org.jooq.SQL;
import org.jooq.Schema;
import org.jooq.ScopedConverter;
import org.jooq.Select;
import org.jooq.SelectField;
import org.jooq.Table;
@ -203,7 +204,7 @@ implements
}
@Override
public final Converter<?, R> getConverter() {
public final ScopedConverter<?, R> getConverter() {
return getDataType().getConverter();
}

View File

@ -46,6 +46,7 @@ import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Generator;
import org.jooq.Name;
import org.jooq.ScopedConverter;
import org.jooq.Typed;
/**
@ -71,7 +72,7 @@ abstract class AbstractTypedNamed<T> extends AbstractNamed implements Typed<T> {
// -------------------------------------------------------------------------
@Override
public final Converter<?, T> getConverter() {
public final ScopedConverter<?, T> getConverter() {
return getDataType().getConverter();
}

View File

@ -56,6 +56,7 @@ import jakarta.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import org.jooq.Converter;
import org.jooq.ConverterScope;
import org.jooq.XML;
/**
@ -80,21 +81,21 @@ public class AbstractXMLasObjectBinding<T> extends AbstractXMLBinding<T> {
return converter;
}
private static final class XMLasObjectConverter<T> implements Converter<XML, T> {
private static final class XMLasObjectConverter<T> extends AbstractScopedConverter<XML, T> {
Class<T> type;
XmlRootElement root;
transient JAXBContext ctx;
XmlRootElement root;
transient JAXBContext ctx;
private XMLasObjectConverter(Class<T> type) {
this.type = type;
super(XML.class, type);
this.root = type.getAnnotation(XmlRootElement.class);
this.ctx = initCtx();
}
private final JAXBContext initCtx() {
try {
return JAXBContext.newInstance(type);
return JAXBContext.newInstance(toType());
}
catch (JAXBException e) {
throw new DataBindingException(e);
@ -102,15 +103,15 @@ public class AbstractXMLasObjectBinding<T> extends AbstractXMLBinding<T> {
}
@Override
public T from(XML t) {
public T from(XML t, ConverterScope scope) {
if (t == null)
return null;
return JAXB.unmarshal(new StringReader("" + t), type);
return JAXB.unmarshal(new StringReader("" + t), toType());
}
@Override
public XML to(T u) {
public XML to(T u, ConverterScope scope) {
if (u == null)
return null;
@ -118,9 +119,8 @@ public class AbstractXMLasObjectBinding<T> extends AbstractXMLBinding<T> {
StringWriter s = new StringWriter();
Object o = u;
if (root == null) {
o = new JAXBElement<>(new QName(decapitalize(type.getSimpleName())), type, u);
}
if (root == null)
o = new JAXBElement<>(new QName(decapitalize(toType().getSimpleName())), toType(), u);
Marshaller m = ctx.createMarshaller();
m.setProperty(Marshaller.JAXB_FRAGMENT, true);
@ -132,16 +132,6 @@ public class AbstractXMLasObjectBinding<T> extends AbstractXMLBinding<T> {
}
}
@Override
public Class<XML> fromType() {
return XML.class;
}
@Override
public Class<T> toType() {
return type;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}

View File

@ -40,7 +40,9 @@ package org.jooq.impl;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.MILLI_OF_DAY;
import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
import static org.jooq.ScopedConverter.scoped;
import static org.jooq.impl.Internal.arrayType;
import static org.jooq.impl.Internal.converterScope;
import static org.jooq.impl.Tools.configuration;
import static org.jooq.impl.Tools.emulateMultiset;
import static org.jooq.tools.reflect.Reflect.accessible;
@ -90,11 +92,10 @@ import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import jakarta.xml.bind.JAXB;
// ...
import org.jooq.Converter;
import org.jooq.ConverterProvider;
import org.jooq.ConverterScope;
import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.JSON;
@ -124,6 +125,8 @@ import org.jooq.types.ULong;
import org.jooq.types.UShort;
import org.jooq.util.xml.jaxb.InformationSchema;
import jakarta.xml.bind.JAXB;
/**
* Utility methods for type conversions
* <p>
@ -400,7 +403,7 @@ final class Convert {
}
static final <U> U[] convertCollection(Collection from, Class<? extends U[]> to){
return new ConvertAll<U[]>(to).from(from);
return new ConvertAll<U[]>(to).from(from, converterScope());
}
/**
@ -430,10 +433,10 @@ final class Convert {
Class<T> fromType = converter.fromType();
if (fromType == Object.class)
return converter.from((T) from);
return scoped(converter).from((T) from, converterScope());
ConvertAll<T> convertAll = new ConvertAll<>(fromType);
return converter.from(convertAll.from(from));
return scoped(converter).from(convertAll.from(from, converterScope()), converterScope());
}
/**
@ -544,7 +547,7 @@ final class Convert {
List<U> result = new ArrayList<>(collection.size());
for (Object o : collection)
result.add(convert(all.from(o), converter));
result.add(convert(all.from(o, converterScope()), converter));
return result;
}
@ -557,17 +560,20 @@ final class Convert {
/**
* The converter to convert them all.
*/
private static class ConvertAll<U> implements Converter<Object, U> {
private static final class ConvertAll<U> extends AbstractScopedConverter<Object, U> {
private final Class<? extends U> toClass;
@SuppressWarnings("unchecked")
ConvertAll(Class<? extends U> toClass) {
super(Object.class, (Class<U>) toClass);
this.toClass = toClass;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public U from(Object from) {
public U from(Object from, ConverterScope scope) {
if (from == null) {
// [#936] If types are converted to primitives, the result must not
@ -608,7 +614,7 @@ final class Convert {
// [#12557] Anything can be unwrapped from Optional
else if (fromClass == Optional.class)
return from(((Optional) from).orElse(null));
return from(((Optional) from).orElse(null), scope);
// Regular checks
else if (fromClass == byte[].class) {
@ -1396,21 +1402,10 @@ final class Convert {
}
@Override
public Object to(U to) {
public Object to(U to, ConverterScope scope) {
return to;
}
@Override
public Class<Object> fromType() {
return Object.class;
}
@SuppressWarnings("unchecked")
@Override
public Class<U> toType() {
return (Class<U>) toClass;
}
/**
* Convert a long timestamp (millis) to any date type.
*/

View File

@ -37,6 +37,8 @@
*/
package org.jooq.impl;
import static org.jooq.impl.Internal.converterScope;
import java.util.List;
import java.util.Map;
@ -54,6 +56,7 @@ import org.jooq.Record;
import org.jooq.Result;
import org.jooq.Row;
import org.jooq.SQLDialect;
import org.jooq.ScopedConverter;
import org.jooq.exception.DataTypeException;
import org.jooq.impl.DefaultBinding.InternalBinding;
import org.jooq.impl.QOM.GenerationLocation;
@ -306,7 +309,7 @@ final class ConvertedDataType<T, U> extends AbstractDataTypeX<U> {
// [#3200] Try to convert arbitrary objects to T
else
return ((Converter<T, U>) getConverter()).from(delegate.convert(object));
return ((ScopedConverter<T, U>) getConverter()).from(delegate.convert(object), converterScope());
}
@Override

View File

@ -84,6 +84,7 @@ import org.jooq.Field;
// ...
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.ScopedConverter;
import org.jooq.exception.ControlFlowSignal;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.jdbc.JDBC41ResultSet;
@ -1570,7 +1571,7 @@ final class CursorImpl<R extends Record> extends AbstractCursor<R> {
// [#7100] TODO: Is there a more elegant way to do this?
if (f != field)
value = ((Converter<Object, T>) field.getConverter()).from(value);
value = ((ScopedConverter<Object, T>) field.getConverter()).from(value, ctx.converterScope());
offset += operation.offset - nestedOffset + nested.size() - 1;
}

View File

@ -42,6 +42,7 @@ import java.time.LocalDate;
import java.util.function.Function;
import org.jooq.Converter;
import org.jooq.ConverterScope;
/**
* @author Lukas Eder
@ -51,19 +52,19 @@ import org.jooq.Converter;
* <code>Converter.ofNullable(Date.class, LocalDate.class, Date::toLocalDate, Date::valueOf)</code>.
*/
@Deprecated
public final class DateToLocalDateConverter extends AbstractConverter<Date, LocalDate> {
public final class DateToLocalDateConverter extends AbstractScopedConverter<Date, LocalDate> {
public DateToLocalDateConverter() {
super(Date.class, LocalDate.class);
}
@Override
public final LocalDate from(Date t) {
public final LocalDate from(Date t, ConverterScope scope) {
return t == null ? null : t.toLocalDate();
}
@Override
public final Date to(LocalDate u) {
public final Date to(LocalDate u, ConverterScope scope) {
return u == null ? null : Date.valueOf(u);
}
}

View File

@ -82,6 +82,7 @@ import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.ScopedConverter.scoped;
import static org.jooq.conf.ParamType.INLINED;
import static org.jooq.impl.Convert.convert;
import static org.jooq.impl.Convert.patchIso8601Timestamp;
@ -211,6 +212,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
@ -223,11 +225,13 @@ import org.jooq.BindingGetSQLInputContext;
import org.jooq.BindingGetStatementContext;
import org.jooq.BindingRegisterContext;
import org.jooq.BindingSQLContext;
import org.jooq.BindingScope;
import org.jooq.BindingSetSQLOutputContext;
import org.jooq.BindingSetStatementContext;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.ConverterScope;
import org.jooq.Converters;
import org.jooq.DataType;
import org.jooq.EnumType;
@ -250,6 +254,7 @@ import org.jooq.RowId;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.Scope;
import org.jooq.ScopedConverter;
import org.jooq.Source;
import org.jooq.Spatial;
import org.jooq.TableRecord;
@ -370,31 +375,31 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
else if (type == LocalDate.class)
return (Binding<T, U>) new DelegatingBinding<>(
(DataType<LocalDate>) dataType,
Converter.ofNullable(Date.class, LocalDate.class,
(Function<Date, LocalDate> & Serializable) Date::toLocalDate,
(Function<LocalDate, Date> & Serializable) Date::valueOf
ScopedConverter.ofNullable(Date.class, LocalDate.class,
(BiFunction<Date, ConverterScope, LocalDate> & Serializable) (t, x) -> t.toLocalDate(),
(BiFunction<LocalDate, ConverterScope, Date> & Serializable) (t, x) -> Date.valueOf(t)
),
(Converter<LocalDate, U>) converter,
(ScopedConverter<LocalDate, U>) converter,
c -> new DefaultDateBinding<>(DATE, c)
);
else if (type == LocalDateTime.class)
return (Binding<T, U>) new DelegatingBinding<>(
(DataType<LocalDateTime>) dataType,
Converter.ofNullable(Timestamp.class, LocalDateTime.class,
(Function<Timestamp, LocalDateTime> & Serializable) Timestamp::toLocalDateTime,
(Function<LocalDateTime, Timestamp> & Serializable) Timestamp::valueOf
ScopedConverter.ofNullable(Timestamp.class, LocalDateTime.class,
(BiFunction<Timestamp, ConverterScope, LocalDateTime> & Serializable) (t, x) -> t.toLocalDateTime(),
(BiFunction<LocalDateTime, ConverterScope, Timestamp> & Serializable) (t, x) -> Timestamp.valueOf(t)
),
(Converter<LocalDateTime, U>) converter,
(ScopedConverter<LocalDateTime, U>) converter,
c -> new DefaultTimestampBinding<>(TIMESTAMP, c)
);
else if (type == LocalTime.class)
return (Binding<T, U>) new DelegatingBinding<>(
(DataType<LocalTime>) dataType,
Converter.ofNullable(Time.class, LocalTime.class,
(Function<Time, LocalTime> & Serializable) Time::toLocalTime,
(Function<LocalTime, Time> & Serializable) Time::valueOf
ScopedConverter.ofNullable(Time.class, LocalTime.class,
(BiFunction<Time, ConverterScope, LocalTime> & Serializable) (t, x) -> t.toLocalTime(),
(BiFunction<LocalTime, ConverterScope, Time> & Serializable) (t, x) -> Time.valueOf(t)
),
(Converter<LocalTime, U>) converter,
(ScopedConverter<LocalTime, U>) converter,
c -> new DefaultTimeBinding<>(TIME, c)
);
else if (type == Long.class || type == long.class)
@ -424,41 +429,41 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
else if (type == UByte.class)
return (Binding<T, U>) new DelegatingBinding<>(
(DataType<UByte>) dataType,
Converter.ofNullable(Short.class, UByte.class,
(Function<Short, UByte> & Serializable) UByte::valueOf,
(Function<UByte, Short> & Serializable) UByte::shortValue
ScopedConverter.ofNullable(Short.class, UByte.class,
(BiFunction<Short, ConverterScope, UByte> & Serializable) (t, x) -> UByte.valueOf(t),
(BiFunction<UByte, ConverterScope, Short> & Serializable) (t, x) -> t.shortValue()
),
(Converter<UByte, U>) converter,
(ScopedConverter<UByte, U>) converter,
c -> new DefaultShortBinding<>(SMALLINT, c)
);
else if (type == UInteger.class)
return (Binding<T, U>) new DelegatingBinding<>(
(DataType<UInteger>) dataType,
Converter.ofNullable(Long.class, UInteger.class,
(Function<Long, UInteger> & Serializable) UInteger::valueOf,
(Function<UInteger, Long> & Serializable) UInteger::longValue
ScopedConverter.ofNullable(Long.class, UInteger.class,
(BiFunction<Long, ConverterScope, UInteger> & Serializable) (t, x) -> UInteger.valueOf(t),
(BiFunction<UInteger, ConverterScope, Long> & Serializable) (t, x) -> t.longValue()
),
(Converter<UInteger, U>) converter,
(ScopedConverter<UInteger, U>) converter,
c -> new DefaultLongBinding<>(BIGINT, c)
);
else if (type == ULong.class)
return (Binding<T, U>) new DelegatingBinding<>(
(DataType<ULong>) dataType,
Converter.ofNullable(BigInteger.class, ULong.class,
(Function<BigInteger, ULong> & Serializable) ULong::valueOf,
(Function<ULong, BigInteger> & Serializable) ULong::toBigInteger
ScopedConverter.ofNullable(BigInteger.class, ULong.class,
(BiFunction<BigInteger, ConverterScope, ULong> & Serializable) (t, x) -> ULong.valueOf(t),
(BiFunction<ULong, ConverterScope, BigInteger> & Serializable) (t, x) -> t.toBigInteger()
),
(Converter<ULong, U>) converter,
(ScopedConverter<ULong, U>) converter,
c -> new DefaultBigIntegerBinding<>(DECIMAL_INTEGER, c)
);
else if (type == UShort.class)
return (Binding<T, U>) new DelegatingBinding<>(
(DataType<UShort>) dataType,
Converter.ofNullable(Integer.class, UShort.class,
(Function<Integer, UShort> & Serializable) UShort::valueOf,
(Function<UShort, Integer> & Serializable) UShort::intValue
ScopedConverter.ofNullable(Integer.class, UShort.class,
(BiFunction<Integer, ConverterScope, UShort> & Serializable) (t, x) -> UShort.valueOf(t),
(BiFunction<UShort, ConverterScope, Integer> & Serializable) (t, x) -> t.intValue()
),
(Converter<UShort, U>) converter,
(ScopedConverter<UShort, U>) converter,
c -> new DefaultIntegerBinding<>(INTEGER, c)
);
else if (type == UUID.class)
@ -515,12 +520,12 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
theBinding = (Binding) binding;
}
else if (binding == null) {
theBinding = binding(dataType, (Converter<T, U>) converter);
theBinding = binding(dataType, (ScopedConverter<T, U>) scoped(converter));
}
else {
theBinding = new Binding<T, U>() {
final Converter<T, U> theConverter = Converters.of(binding.converter(), converter);
final ScopedConverter<T, U> theConverter = Converters.of(binding.converter(), converter);
@Override
public Converter<T, U> converter() {
@ -696,12 +701,12 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
final DataType<T> dataType;
final Converter<T, U> converter;
final ScopedConverter<T, U> converter;
final boolean attachable;
InternalBinding(DataType<T> dataType, Converter<T, U> converter) {
this.dataType = dataType;
this.converter = converter;
this.converter = ScopedConverter.scoped(converter);
// [#11099] Caching this per binding seems to have a considerable performance effect.
// We must be careful to short circuit instanceof Attachable checks only if we *know*
@ -711,7 +716,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
}
@Override
public final Converter<T, U> converter() {
public final ScopedConverter<T, U> converter() {
return converter;
}
@ -935,7 +940,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
public final void sql(BindingSQLContext<U> ctx) throws SQLException {
T converted = converter().to(ctx.value());
T converted = converter().to(ctx.value(), ctx.converterScope());
// Casting can be enforced or prevented
switch (ctx.render().castMode()) {
@ -995,7 +1000,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
public final void set(BindingSetStatementContext<U> ctx) throws SQLException {
T value = converter().to(ctx.value());
T value = converter().to(ctx.value(), ctx.converterScope());
if (!FALSE.equals(ctx.settings().isExecuteLogging()))
if (log.isTraceEnabled())
@ -1012,7 +1017,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
public final void set(BindingSetSQLOutputContext<U> ctx) throws SQLException {
T value = converter().to(ctx.value());
T value = converter().to(ctx.value(), ctx.converterScope());
if (value == null)
ctx.output().writeObject(null);
@ -1022,7 +1027,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
public final void get(BindingGetResultSetContext<U> ctx) throws SQLException {
U value = converter().from(get0(ctx));
U value = converter().from(get0(ctx), ctx.converterScope());
if (attachable)
value = attach(value, ctx.configuration());
@ -1032,7 +1037,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
public final void get(BindingGetStatementContext<U> ctx) throws SQLException {
U value = converter().from(get0(ctx));
U value = converter().from(get0(ctx), ctx.converterScope());
if (attachable)
value = attach(value, ctx.configuration());
@ -1042,7 +1047,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
public final void get(BindingGetSQLInputContext<U> ctx) throws SQLException {
U value = converter().from(get0(ctx));
U value = converter().from(get0(ctx), ctx.converterScope());
if (attachable)
value = attach(value, ctx.configuration());
@ -1107,14 +1112,14 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
static final class DelegatingBinding<X, T, U> extends InternalBinding<X, U> {
private final Converter<T, X> delegatingConverter;
private final ScopedConverter<T, X> delegatingConverter;
private final InternalBinding<T, U> delegatingBinding;
DelegatingBinding(
DataType<X> originalDataType,
Converter<T, X> delegatingConverter,
Converter<X, U> originalConverter,
Function<? super Converter<T, U>, ? extends InternalBinding<T, U>> f
ScopedConverter<T, X> delegatingConverter,
ScopedConverter<X, U> originalConverter,
Function<? super ScopedConverter<T, U>, ? extends InternalBinding<T, U>> f
) {
super(originalDataType, originalConverter);
@ -1124,17 +1129,17 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final void sqlInline0(BindingSQLContext<U> ctx, X value) throws SQLException {
delegatingBinding.sqlInline0(ctx, delegatingConverter.to(value));
delegatingBinding.sqlInline0(ctx, delegatingConverter.to(value, ctx.converterScope()));
}
@Override
final void sqlBind0(BindingSQLContext<U> ctx, X value) throws SQLException {
delegatingBinding.sqlBind0(ctx, delegatingConverter.to(value));
delegatingBinding.sqlBind0(ctx, delegatingConverter.to(value, ctx.converterScope()));
}
@Override
final void set0(BindingSetStatementContext<U> ctx, X value) throws SQLException {
delegatingBinding.set0(ctx, delegatingConverter.to(value));
delegatingBinding.set0(ctx, delegatingConverter.to(value, ctx.converterScope()));
}
@Override
@ -1144,22 +1149,22 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final void set0(BindingSetSQLOutputContext<U> ctx, X value) throws SQLException {
delegatingBinding.set0(ctx, delegatingConverter.to(value));
delegatingBinding.set0(ctx, delegatingConverter.to(value, ctx.converterScope()));
}
@Override
final X get0(BindingGetResultSetContext<U> ctx) throws SQLException {
return delegatingConverter.from(delegatingBinding.get0(ctx));
return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterScope());
}
@Override
final X get0(BindingGetStatementContext<U> ctx) throws SQLException {
return delegatingConverter.from(delegatingBinding.get0(ctx));
return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterScope());
}
@Override
final X get0(BindingGetSQLInputContext<U> ctx) throws SQLException {
return delegatingConverter.from(delegatingBinding.get0(ctx));
return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterScope());
}
@Override
@ -3454,11 +3459,12 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
static final class DefaultInstantBinding<U> extends InternalBinding<Instant, U> {
private static final Converter<OffsetDateTime, Instant> CONVERTER = Converter.ofNullable(
@SuppressWarnings("unchecked")
private static final ScopedConverter<OffsetDateTime, Instant> CONVERTER = ScopedConverter.ofNullable(
OffsetDateTime.class,
Instant.class,
(Function<OffsetDateTime, Instant> & Serializable) OffsetDateTime::toInstant,
(Function<Instant, OffsetDateTime> & Serializable) i -> OffsetDateTime.ofInstant(i, ZoneOffset.UTC)
(BiFunction<OffsetDateTime, ConverterScope, Instant> & Serializable) (t, x) -> t.toInstant(),
(BiFunction<Instant, ConverterScope, OffsetDateTime> & Serializable) (i, x) -> OffsetDateTime.ofInstant(i, ZoneOffset.UTC)
);
private final DefaultOffsetDateTimeBinding<U> delegate;
@ -3476,32 +3482,32 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final void sqlInline0(BindingSQLContext<U> ctx, Instant value) throws SQLException {
delegate.sqlInline0(ctx, CONVERTER.to(value));
delegate.sqlInline0(ctx, CONVERTER.to(value, ctx.converterScope()));
}
@Override
final void set0(BindingSetStatementContext<U> ctx, Instant value) throws SQLException {
delegate.set0(ctx, CONVERTER.to(value));
delegate.set0(ctx, CONVERTER.to(value, ctx.converterScope()));
}
@Override
final void set0(BindingSetSQLOutputContext<U> ctx, Instant value) throws SQLException {
delegate.set0(ctx, CONVERTER.to(value));
delegate.set0(ctx, CONVERTER.to(value, ctx.converterScope()));
}
@Override
final Instant get0(BindingGetResultSetContext<U> ctx) throws SQLException {
return CONVERTER.from(delegate.get0(ctx));
return CONVERTER.from(delegate.get0(ctx), ctx.converterScope());
}
@Override
final Instant get0(BindingGetStatementContext<U> ctx) throws SQLException {
return CONVERTER.from(delegate.get0(ctx));
return CONVERTER.from(delegate.get0(ctx), ctx.converterScope());
}
@Override
final Instant get0(BindingGetSQLInputContext<U> ctx) throws SQLException {
return CONVERTER.from(delegate.get0(ctx));
return CONVERTER.from(delegate.get0(ctx), ctx.converterScope());
}
@Override
@ -3841,8 +3847,8 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
}
@SuppressWarnings("unchecked")
private static final <T, U> U pgFromString(Scope ctx, Field<U> field, String string) {
Converter<T, U> converter = (Converter<T, U>) field.getConverter();
private static final <T, U> U pgFromString(BindingScope ctx, Field<U> field, String string) {
ScopedConverter<T, U> converter = (ScopedConverter<T, U>) field.getConverter();
Class<?> type = wrapper(converter.fromType());
if (string == null)
@ -3850,88 +3856,88 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
else if (type == Blob.class)
; // Not supported
else if (type == Boolean.class)
return converter.from((T) Convert.convert(string, Boolean.class));
return converter.from((T) Convert.convert(string, Boolean.class), ctx.converterScope());
else if (type == BigInteger.class)
return converter.from((T) new BigInteger(string));
return converter.from((T) new BigInteger(string), ctx.converterScope());
else if (type == BigDecimal.class)
return converter.from((T) new BigDecimal(string));
return converter.from((T) new BigDecimal(string), ctx.converterScope());
else if (type == Byte.class)
return converter.from((T) Byte.valueOf(string));
return converter.from((T) Byte.valueOf(string), ctx.converterScope());
else if (type == byte[].class)
return converter.from((T) PostgresUtils.toBytes(string));
return converter.from((T) PostgresUtils.toBytes(string), ctx.converterScope());
else if (type == Clob.class)
; // Not supported
else if (type == Date.class)
return converter.from((T) Date.valueOf(string));
return converter.from((T) Date.valueOf(string), ctx.converterScope());
else if (type == Double.class)
return converter.from((T) Double.valueOf(string));
return converter.from((T) Double.valueOf(string), ctx.converterScope());
else if (type == Float.class)
return converter.from((T) Float.valueOf(string));
return converter.from((T) Float.valueOf(string), ctx.converterScope());
else if (type == Integer.class)
return converter.from((T) Integer.valueOf(string));
return converter.from((T) Integer.valueOf(string), ctx.converterScope());
else if (type == Long.class)
return converter.from((T) Long.valueOf(string));
return converter.from((T) Long.valueOf(string), ctx.converterScope());
else if (type == Short.class)
return converter.from((T) Short.valueOf(string));
return converter.from((T) Short.valueOf(string), ctx.converterScope());
else if (type == String.class)
return converter.from((T) string);
return converter.from((T) string, ctx.converterScope());
else if (type == Time.class)
return converter.from((T) Time.valueOf(string));
return converter.from((T) Time.valueOf(string), ctx.converterScope());
else if (type == Timestamp.class)
return converter.from((T) Timestamp.valueOf(patchIso8601Timestamp(string, false)));
return converter.from((T) Timestamp.valueOf(patchIso8601Timestamp(string, false)), ctx.converterScope());
else if (type == LocalTime.class)
return converter.from((T) LocalTime.parse(string));
return converter.from((T) LocalTime.parse(string), ctx.converterScope());
else if (type == LocalDate.class)
return converter.from((T) LocalDate.parse(string));
return converter.from((T) LocalDate.parse(string), ctx.converterScope());
else if (type == LocalDateTime.class)
return converter.from((T) LocalDateTime.parse(patchIso8601Timestamp(string, true)));
return converter.from((T) LocalDateTime.parse(patchIso8601Timestamp(string, true)), ctx.converterScope());
else if (type == OffsetTime.class)
return converter.from((T) OffsetDateTimeParser.offsetTime(string));
return converter.from((T) OffsetDateTimeParser.offsetTime(string), ctx.converterScope());
else if (type == OffsetDateTime.class)
return converter.from((T) OffsetDateTimeParser.offsetDateTime(string));
return converter.from((T) OffsetDateTimeParser.offsetDateTime(string), ctx.converterScope());
else if (type == Instant.class)
return converter.from((T) OffsetDateTimeParser.offsetDateTime(string).toInstant());
return converter.from((T) OffsetDateTimeParser.offsetDateTime(string).toInstant(), ctx.converterScope());
else if (type == JSON.class)
return converter.from((T) JSON.json(string));
return converter.from((T) JSON.json(string), ctx.converterScope());
else if (type == JSONB.class)
return converter.from((T) JSONB.jsonb(string));
return converter.from((T) JSONB.jsonb(string), ctx.converterScope());
else if (type == UByte.class)
return converter.from((T) UByte.valueOf(string));
return converter.from((T) UByte.valueOf(string), ctx.converterScope());
else if (type == UShort.class)
return converter.from((T) UShort.valueOf(string));
return converter.from((T) UShort.valueOf(string), ctx.converterScope());
else if (type == UInteger.class)
return converter.from((T) UInteger.valueOf(string));
return converter.from((T) UInteger.valueOf(string), ctx.converterScope());
else if (type == ULong.class)
return converter.from((T) ULong.valueOf(string));
return converter.from((T) ULong.valueOf(string), ctx.converterScope());
else if (type == UUID.class)
return converter.from((T) UUID.fromString(string));
return converter.from((T) UUID.fromString(string), ctx.converterScope());
else if (type == XML.class)
return converter.from((T) XML.xml(string));
return converter.from((T) XML.xml(string), ctx.converterScope());
else if (type.isArray())
return converter.from((T) pgNewArray(ctx, field, type, string));
return converter.from((T) pgNewArray(ctx, field, type, string), ctx.converterScope());
else if (EnumType.class.isAssignableFrom(type))
return converter.from((T) DefaultEnumTypeBinding.getEnumType((Class<EnumType>) type, string));
return converter.from((T) DefaultEnumTypeBinding.getEnumType((Class<EnumType>) type, string), ctx.converterScope());
else if (Result.class.isAssignableFrom(type))
if (string.startsWith("<"))
return converter.from((T) readMultisetXML(ctx, (AbstractRow<Record>) field.getDataType().getRow(), (Class<Record>) field.getDataType().getRecordType(), string));
return converter.from((T) readMultisetXML(ctx, (AbstractRow<Record>) field.getDataType().getRow(), (Class<Record>) field.getDataType().getRecordType(), string), ctx.converterScope());
else
return converter.from((T) readMultisetJSON(ctx, (AbstractRow<Record>) field.getDataType().getRow(), (Class<Record>) field.getDataType().getRecordType(), string));
return converter.from((T) readMultisetJSON(ctx, (AbstractRow<Record>) field.getDataType().getRow(), (Class<Record>) field.getDataType().getRecordType(), string), ctx.converterScope());
else if (Record.class.isAssignableFrom(type)
// [#11812] UDTRecords/TableRecords or InternalRecords that don't have an explicit converter
&& (!InternalRecord.class.isAssignableFrom(type) || type == converter.fromType()))
return converter.from((T) pgNewRecord(ctx, type, (AbstractRow<?>) field.getDataType().getRow(), string));
return converter.from((T) pgNewRecord(ctx, type, (AbstractRow<?>) field.getDataType().getRow(), string), ctx.converterScope());
else if (type == Object.class)
return converter.from((T) string);
return converter.from((T) string, ctx.converterScope());
// [#4964] [#6058] Recurse only if we have a meaningful converter, not the identity converter,
// which would cause a StackOverflowError, here!
else if (type != wrapper(converter.toType())) {
return converter.from((T) pgFromString(ctx, field("converted_field", ((ConvertedDataType<?, ?>) field.getDataType()).delegate()), string));
return converter.from((T) pgFromString(ctx, field("converted_field", ((ConvertedDataType<?, ?>) field.getDataType()).delegate()), string), ctx.converterScope());
}
throw new UnsupportedOperationException("Class " + type + " is not supported");
@ -3949,7 +3955,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
* @return The converted {@link UDTRecord}
*/
@SuppressWarnings("unchecked")
static final Record pgNewRecord(Scope ctx, Class<?> type, AbstractRow<?> fields, Object object) {
static final Record pgNewRecord(BindingScope ctx, Class<?> type, AbstractRow<?> fields, Object object) {
if (object == null)
return null;
@ -3980,7 +3986,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
});
}
private static final <T> void pgSetValue(Scope ctx, Record record, Field<T> field, String value) {
private static final <T> void pgSetValue(BindingScope ctx, Record record, Field<T> field, String value) {
record.set(field, pgFromString(ctx, field, value));
}
@ -3993,7 +3999,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
* @param string A String representation of an array
* @return The converted array
*/
private static final Object[] pgNewArray(Scope ctx, Field<?> field, Class<?> type, String string) {
private static final Object[] pgNewArray(BindingScope ctx, Field<?> field, Class<?> type, String string) {
if (string == null)
return null;
@ -5410,7 +5416,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
void sqlInline0(BindingSQLContext<U> ctx, JSONB value) throws SQLException {
if (EMULATE_AS_BLOB.contains(ctx.dialect())) {
bytes(ctx.configuration()).sqlInline0(ctx, bytesConverter(ctx.configuration()).to(value));
bytes(ctx.configuration()).sqlInline0(ctx, bytesConverter(ctx.configuration()).to(value, ctx.converterScope()));
}
else {
super.sqlInline0(ctx, value);
@ -5431,7 +5437,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final void set0(BindingSetStatementContext<U> ctx, JSONB value) throws SQLException {
if (EMULATE_AS_BLOB.contains(ctx.dialect()))
bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value));
bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value, ctx.converterScope()));
else
ctx.statement().setString(ctx.index(), value.data());
}
@ -5439,7 +5445,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final void set0(BindingSetSQLOutputContext<U> ctx, JSONB value) throws SQLException {
if (EMULATE_AS_BLOB.contains(ctx.dialect()))
bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value));
bytes(ctx.configuration()).set0(ctx, bytesConverter(ctx.configuration()).to(value, ctx.converterScope()));
else
ctx.output().writeString(value.data());
}
@ -5447,7 +5453,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final JSONB get0(BindingGetResultSetContext<U> ctx) throws SQLException {
if (EMULATE_AS_BLOB.contains(ctx.dialect()))
return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx));
return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx), ctx.converterScope());
String string = ctx.resultSet().getString(ctx.index());
return string == null ? null : JSONB.valueOf(string);
@ -5456,7 +5462,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final JSONB get0(BindingGetStatementContext<U> ctx) throws SQLException {
if (EMULATE_AS_BLOB.contains(ctx.dialect()))
return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx));
return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx), ctx.converterScope());
String string = ctx.statement().getString(ctx.index());
return string == null ? null : JSONB.valueOf(string);
@ -5465,7 +5471,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
@Override
final JSONB get0(BindingGetSQLInputContext<U> ctx) throws SQLException {
if (EMULATE_AS_BLOB.contains(ctx.dialect()))
return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx));
return bytesConverter(ctx.configuration()).from(bytes(ctx.configuration()).get0(ctx), ctx.converterScope());
String string = ctx.input().readString();
return string == null ? null : JSONB.valueOf(string);
@ -5479,10 +5485,10 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
return Types.VARCHAR;
}
private final Converter<byte[], JSONB> bytesConverter(final Configuration configuration) {
return Converter.ofNullable(byte[].class, JSONB.class,
t -> JSONB.valueOf(new String(t, configuration.charsetProvider().provide())),
u -> u.toString().getBytes(configuration.charsetProvider().provide())
private final ScopedConverter<byte[], JSONB> bytesConverter(final Configuration configuration) {
return ScopedConverter.ofNullable(byte[].class, JSONB.class,
(t, x) -> JSONB.valueOf(new String(t, configuration.charsetProvider().provide())),
(u, x) -> u.toString().getBytes(configuration.charsetProvider().provide())
);
}

View File

@ -37,6 +37,8 @@
*/
package org.jooq.impl;
import static org.jooq.ScopedConverter.scoped;
import java.sql.ResultSet;
import org.jooq.BindingGetResultSetContext;
@ -101,7 +103,7 @@ class DefaultBindingGetResultSetContext<U> extends AbstractExecuteScope implemen
return new DefaultBindingGetResultSetContext<T>(ctx, resultSet, index) {
@Override
public void value(T v) {
outer.value(converter.from(v));
outer.value(scoped(converter).from(v, converterScope()));
}
};
}

View File

@ -37,6 +37,8 @@
*/
package org.jooq.impl;
import static org.jooq.ScopedConverter.scoped;
import java.sql.SQLInput;
import org.jooq.BindingGetSQLInputContext;
@ -78,7 +80,7 @@ class DefaultBindingGetSQLInputContext<U> extends AbstractExecuteScope implement
return new DefaultBindingGetSQLInputContext<T>(ctx, input) {
@Override
public void value(T v) {
outer.value(converter.from(v));
outer.value(scoped(converter).from(v, converterScope()));
}
};
}

View File

@ -37,11 +37,14 @@
*/
package org.jooq.impl;
import static org.jooq.ScopedConverter.scoped;
import java.sql.CallableStatement;
import org.jooq.BindingGetStatementContext;
import org.jooq.Converter;
import org.jooq.ExecuteContext;
import org.jooq.ScopedConverter;
/**
* @author Lukas Eder
@ -85,7 +88,7 @@ class DefaultBindingGetStatementContext<U> extends AbstractExecuteScope implemen
return new DefaultBindingGetStatementContext<T>(ctx, statement, index) {
@Override
public void value(T v) {
outer.value(converter.from(v));
outer.value(scoped(converter).from(v, converterScope()));
}
};
}

View File

@ -37,11 +37,14 @@
*/
package org.jooq.impl;
import static org.jooq.ScopedConverter.scoped;
import java.util.Map;
import org.jooq.BindingSQLContext;
import org.jooq.Configuration;
import org.jooq.Converter;
import org.jooq.ConverterScope;
import org.jooq.RenderContext;
/**
@ -65,6 +68,11 @@ class DefaultBindingSQLContext<U> extends AbstractScope implements BindingSQLCon
this.variable = variable;
}
@Override
public final ConverterScope converterScope() {
return render.converterScope();
}
@Override
public final RenderContext render() {
return render;
@ -82,7 +90,7 @@ class DefaultBindingSQLContext<U> extends AbstractScope implements BindingSQLCon
@Override
public <T> BindingSQLContext<T> convert(Converter<? extends T, ? super U> converter) {
return new DefaultBindingSQLContext<>(configuration, data, render, converter.to(value), variable);
return new DefaultBindingSQLContext<>(configuration, data, render, scoped(converter).to(value, converterScope()), variable);
}
@Override

View File

@ -37,6 +37,8 @@
*/
package org.jooq.impl;
import static org.jooq.ScopedConverter.scoped;
import java.sql.SQLOutput;
import org.jooq.BindingSetSQLOutputContext;
@ -70,7 +72,7 @@ class DefaultBindingSetSQLOutputContext<U> extends AbstractExecuteScope implemen
@Override
public final <T> BindingSetSQLOutputContext<T> convert(Converter<? extends T, ? super U> converter) {
return new DefaultBindingSetSQLOutputContext<>(ctx, output, converter.to(value));
return new DefaultBindingSetSQLOutputContext<>(ctx, output, scoped(converter).to(value, converterScope()));
}
@Override

View File

@ -37,6 +37,8 @@
*/
package org.jooq.impl;
import static org.jooq.ScopedConverter.scoped;
import java.sql.PreparedStatement;
import org.jooq.BindingSetStatementContext;
@ -77,7 +79,7 @@ class DefaultBindingSetStatementContext<U> extends AbstractExecuteScope implemen
@Override
public final <T> BindingSetStatementContext<T> convert(Converter<? extends T, ? super U> converter) {
return new DefaultBindingSetStatementContext<>(ctx, statement, index, converter.to(value));
return new DefaultBindingSetStatementContext<>(ctx, statement, index, scoped(converter).to(value, converterScope()));
}
@Override

View File

@ -0,0 +1,57 @@
/*
* 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.util.Map;
import org.jooq.Configuration;
import org.jooq.ConverterScope;
/**
* @author Lukas Eder
*/
final class DefaultConverterScope extends AbstractScope implements ConverterScope {
DefaultConverterScope(Configuration configuration) {
super(configuration, new DataMap());
}
DefaultConverterScope(Configuration configuration, Map<Object, Object> data) {
super(configuration, data);
}
}

View File

@ -61,6 +61,7 @@ import java.util.Map;
import org.jooq.Configuration;
import org.jooq.ConnectionProvider;
import org.jooq.Constants;
import org.jooq.ConverterScope;
import org.jooq.DDLQuery;
import org.jooq.DSLContext;
import org.jooq.Delete;
@ -94,6 +95,7 @@ class DefaultExecuteContext implements ExecuteContext {
private static final JooqLogger log = JooqLogger.getLogger(DefaultExecuteContext.class);
// Persistent attributes (repeatable)
private final ConverterScope converterScope;
private final Instant creationTime;
private final Configuration originalConfiguration;
private final Configuration derivedConfiguration;
@ -335,6 +337,7 @@ class DefaultExecuteContext implements ExecuteContext {
this.data = new DataMap();
this.query = query;
this.routine = routine;
this.converterScope = new DefaultConverterScope(derivedConfiguration, data);
if (routine != null) {
this.batch = false;
@ -366,6 +369,11 @@ class DefaultExecuteContext implements ExecuteContext {
clean();
}
@Override
public final ConverterScope converterScope() {
return converterScope;
}
@Override
public final Instant creationTime() {
return creationTime;

View File

@ -37,26 +37,37 @@
*/
package org.jooq.impl;
import org.jooq.Converter;
import org.jooq.ConverterScope;
import org.jooq.ScopedConverter;
/**
* @author Lukas Eder
*/
public class DelegatingConverter<T, U> extends AbstractConverter<T, U> {
public class DelegatingConverter<T, U> extends AbstractScopedConverter<T, U> {
private final Converter<T, U> delegate;
private final ScopedConverter<T, U> delegate;
public DelegatingConverter(Converter<T, U> delegate) {
public DelegatingConverter(ScopedConverter<T, U> delegate) {
super(delegate.fromType(), delegate.toType());
this.delegate = delegate;
}
@Override
public final U from(T t, ConverterScope scope) {
return delegate.from(t, scope);
}
@Override
public final U from(T t) {
return delegate.from(t);
}
@Override
public final T to(U u, ConverterScope scope) {
return delegate.to(u, scope);
}
@Override
public final T to(U u) {
return delegate.to(u);

View File

@ -55,6 +55,7 @@ import org.jooq.Function1;
import org.jooq.Function2;
import org.jooq.Name;
import org.jooq.Schema;
import org.jooq.ScopedConverter;
import org.jooq.QueryPart;
import org.jooq.impl.QOM.UNotYetImplemented;
@ -92,7 +93,7 @@ class DomainImpl<T> extends AbstractNamed implements Domain<T>, UNotYetImplement
}
@Override
public final Converter<?, T> getConverter() {
public final ScopedConverter<?, T> getConverter() {
return type.getConverter();
}

View File

@ -38,18 +38,21 @@
package org.jooq.impl;
import static org.jooq.impl.Convert.convert;
import static org.jooq.impl.Internal.converterScope;
import static org.jooq.tools.reflect.Reflect.wrapper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import org.jooq.ConverterScope;
/**
* A base class for enum conversion.
*
* @author Lukas Eder
*/
public class EnumConverter<T, U extends Enum<U>> extends AbstractConverter<T, U> {
public /* non-final */ class EnumConverter<T, U extends Enum<U>> extends AbstractConverter<T, U> {
private final Map<T, U> lookup;
private final Function<? super U, ? extends T> to;
@ -77,8 +80,8 @@ public class EnumConverter<T, U extends Enum<U>> extends AbstractConverter<T, U>
}
@Override
public final U from(T databaseObject) {
return lookup.get(databaseObject);
public final U from(T t) {
return lookup.get(t);
}
/**
@ -88,11 +91,11 @@ public class EnumConverter<T, U extends Enum<U>> extends AbstractConverter<T, U>
* {@inheritDoc}
*/
@Override
public T to(U userObject) {
if (userObject == null)
public T to(U u) {
if (u == null)
return null;
else
return to.apply(userObject);
return to.apply(u);
}
@Override

View File

@ -38,13 +38,15 @@
package org.jooq.impl;
import org.jooq.Converter;
import org.jooq.ConverterScope;
import org.jooq.ScopedConverter;
/**
* A converter that doesn't convert anything.
*
* @author Lukas Eder
*/
public final class IdentityConverter<T> implements Converter<T, T> {
public final class IdentityConverter<T> implements ScopedConverter<T, T> {
private final Class<T> type;
public IdentityConverter(Class<T> type) {
@ -52,12 +54,12 @@ public final class IdentityConverter<T> implements Converter<T, T> {
}
@Override
public final T from(T t) {
public final T from(T t, ConverterScope scope) {
return t;
}
@Override
public final T to(T t) {
public final T to(T t, ConverterScope scope) {
return t;
}

View File

@ -53,6 +53,7 @@ import org.jooq.Binding;
import org.jooq.Check;
import org.jooq.Comment;
import org.jooq.Converter;
import org.jooq.ConverterScope;
import org.jooq.DDLExportConfiguration;
import org.jooq.DataType;
import org.jooq.Domain;
@ -628,4 +629,10 @@ public final class Internal {
else
return 0x7FFFFFF & object.hashCode();
}
private static final ConverterScope CONVERTER_SCOPE = new DefaultConverterScope(CONFIG);
public static final ConverterScope converterScope() {
return CONVERTER_SCOPE;
}
}

View File

@ -41,6 +41,7 @@ import java.lang.reflect.Method;
import jakarta.persistence.AttributeConverter;
import org.jooq.ConverterScope;
import org.jooq.exception.MappingException;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.reflect.Reflect;
@ -58,8 +59,9 @@ import org.jooq.tools.reflect.Reflect;
*
* @author Lukas Eder
*/
public final class JPAConverter<T, U> extends AbstractConverter<T, U> {
private static final JooqLogger log = JooqLogger.getLogger(JPAConverter.class);
public final class JPAConverter<T, U> extends AbstractScopedConverter<T, U> {
private static final JooqLogger log = JooqLogger.getLogger(JPAConverter.class);
private final AttributeConverter<U, T> delegate;
@ -115,12 +117,12 @@ public final class JPAConverter<T, U> extends AbstractConverter<T, U> {
}
@Override
public final U from(T t) {
public final U from(T t, ConverterScope scope) {
return delegate.convertToEntityAttribute(t);
}
@Override
public final T to(U u) {
public final T to(U u, ConverterScope scope) {
return delegate.convertToDatabaseColumn(u);
}
}

View File

@ -37,11 +37,14 @@
*/
package org.jooq.impl;
import static org.jooq.impl.Internal.converterScope;
import org.jooq.Binding;
import org.jooq.Configuration;
import org.jooq.Converter;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.ScopedConverter;
/**
* @author Lukas Eder
@ -96,7 +99,7 @@ final class LegacyConvertedDataType<T, U> extends DefaultDataType<U> {
// [#3200] Try to convert arbitrary objects to T
else
return ((Converter<T, U>) getConverter()).from(delegate.convert(object));
return ((ScopedConverter<T, U>) getConverter()).from(delegate.convert(object), converterScope());
}
@SuppressWarnings({ "unchecked", "rawtypes" })

View File

@ -38,11 +38,13 @@
package org.jooq.impl;
// ...
import static org.jooq.ScopedConverter.scoped;
import static org.jooq.conf.ParamType.NAMED;
import static org.jooq.impl.Internal.subscriber;
import static org.jooq.impl.Tools.EMPTY_PARAM;
import static org.jooq.impl.Tools.abstractDMLQuery;
import static org.jooq.impl.Tools.abstractResultQuery;
import static org.jooq.impl.Tools.converterScope;
import static org.jooq.impl.Tools.fields;
import static org.jooq.impl.Tools.recordFactory;
import static org.jooq.impl.Tools.translate;
@ -91,6 +93,7 @@ import org.jooq.Param;
import org.jooq.Query;
import org.jooq.Record;
import org.jooq.SQLDialect;
import org.jooq.ScopedConverter;
import org.jooq.TransactionalPublishable;
import org.jooq.XML;
import org.jooq.conf.Settings;
@ -1237,7 +1240,7 @@ final class R2DBC {
if (converter == null)
throw new DataTypeException("Cannot convert from " + o.getClass() + " to " + uType + ". Please report an issue here: https://github.com/jOOQ/jOOQ/issues/new. As a workaround, you can implement a ConverterProvider.");
else
return converter.from(o);
return scoped(converter).from(o, converterScope(c));
}
// ---------------------------------------------------------------------

View File

@ -42,11 +42,11 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.time.Instant;
import java.util.Map;
import org.jooq.Configuration;
import org.jooq.ConnectionProvider;
import org.jooq.ConverterScope;
import org.jooq.ExecuteContext;
import org.jooq.ExecuteType;
import org.jooq.Query;
@ -67,6 +67,11 @@ final class SimpleExecuteContext extends AbstractScope implements ExecuteContext
super(configuration, data);
}
@Override
public final ConverterScope converterScope() {
return Tools.converterScope(configuration);
}
@Override
public final Connection connection() {
throw new UnsupportedOperationException("Not implemented");

View File

@ -42,6 +42,7 @@ import java.time.LocalTime;
import java.util.function.Function;
import org.jooq.Converter;
import org.jooq.ConverterScope;
/**
* @author Lukas Eder
@ -51,19 +52,19 @@ import org.jooq.Converter;
* <code>Converter.ofNullable(Time.class, LocalTime.class, Time::toLocalTime, Time::valueOf)</code>.
*/
@Deprecated
public final class TimeToLocalTimeConverter extends AbstractConverter<Time, LocalTime> {
public final class TimeToLocalTimeConverter extends AbstractScopedConverter<Time, LocalTime> {
public TimeToLocalTimeConverter() {
super(Time.class, LocalTime.class);
}
@Override
public final LocalTime from(Time t) {
public final LocalTime from(Time t, ConverterScope scope) {
return t == null ? null : t.toLocalTime();
}
@Override
public final Time to(LocalTime u) {
public final Time to(LocalTime u, ConverterScope scope) {
return u == null ? null : Time.valueOf(u);
}
}

View File

@ -39,23 +39,26 @@ package org.jooq.impl;
import java.sql.Timestamp;
import org.jooq.ConverterScope;
/**
* @author Lukas Eder
*/
final class TimestampToJavaUtilDateConverter extends AbstractConverter<Timestamp, java.util.Date> {
static final TimestampToJavaUtilDateConverter INSTANCE = new TimestampToJavaUtilDateConverter();
final class TimestampToJavaUtilDateConverter extends AbstractScopedConverter<Timestamp, java.util.Date> {
static final TimestampToJavaUtilDateConverter INSTANCE = new TimestampToJavaUtilDateConverter();
private TimestampToJavaUtilDateConverter () {
super(Timestamp.class, java.util.Date.class);
}
@Override
public final java.util.Date from(Timestamp t) {
public final java.util.Date from(Timestamp t, ConverterScope scope) {
return t == null ? null : new java.util.Date(t.getTime());
}
@Override
public final Timestamp to(java.util.Date u) {
public final Timestamp to(java.util.Date u, ConverterScope scope) {
return u == null ? null : new Timestamp(u.getTime());
}
}

View File

@ -42,6 +42,7 @@ import java.time.LocalDateTime;
import java.util.function.Function;
import org.jooq.Converter;
import org.jooq.ConverterScope;
/**
* @author Lukas Eder
@ -51,19 +52,19 @@ import org.jooq.Converter;
* <code>Converter.ofNullable(Timestamp.class, LocalDateTime.class, Timestamp::toLocalDateTime, Timestamp::valueOf)</code>.
*/
@Deprecated
public final class TimestampToLocalDateTimeConverter extends AbstractConverter<Timestamp, LocalDateTime> {
public final class TimestampToLocalDateTimeConverter extends AbstractScopedConverter<Timestamp, LocalDateTime> {
public TimestampToLocalDateTimeConverter() {
super(Timestamp.class, LocalDateTime.class);
}
@Override
public final LocalDateTime from(Timestamp t) {
public final LocalDateTime from(Timestamp t, ConverterScope scope) {
return t == null ? null : t.toLocalDateTime();
}
@Override
public final Timestamp to(LocalDateTime u) {
public final Timestamp to(LocalDateTime u, ConverterScope scope) {
return u == null ? null : Timestamp.valueOf(u);
}
}

View File

@ -75,6 +75,7 @@ import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.ScopedConverter.scoped;
import static org.jooq.conf.BackslashEscaping.DEFAULT;
import static org.jooq.conf.BackslashEscaping.ON;
import static org.jooq.conf.ParamType.INLINED;
@ -219,7 +220,6 @@ import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
@ -263,6 +263,7 @@ import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.ConverterProvider;
import org.jooq.ConverterScope;
import org.jooq.Converters;
import org.jooq.Cursor;
import org.jooq.DSLContext;
@ -294,7 +295,6 @@ import org.jooq.Record1;
import org.jooq.RecordQualifier;
import org.jooq.RenderContext;
import org.jooq.RenderContext.CastMode;
// ...
import org.jooq.Result;
import org.jooq.ResultOrRows;
import org.jooq.ResultQuery;
@ -302,8 +302,8 @@ import org.jooq.Results;
import org.jooq.Row;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.SchemaMapping;
import org.jooq.Scope;
import org.jooq.ScopedConverter;
import org.jooq.Select;
import org.jooq.SelectFieldOrAsterisk;
import org.jooq.SortField;
@ -311,7 +311,6 @@ import org.jooq.Source;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableRecord;
// ...
import org.jooq.UDT;
import org.jooq.UpdatableRecord;
import org.jooq.WindowSpecification;
@ -334,7 +333,6 @@ import org.jooq.exception.MappingException;
import org.jooq.exception.NoDataFoundException;
import org.jooq.exception.TemplatingException;
import org.jooq.exception.TooManyRowsException;
import org.jooq.impl.QOM.GenerationOption;
import org.jooq.impl.QOM.UEmpty;
import org.jooq.impl.ResultsImpl.ResultOrRowsImpl;
import org.jooq.tools.Ints;
@ -1466,7 +1464,7 @@ final class Tools {
* Get a converter from a {@link ConverterProvider} or <code>null</code> if
* no converter could be provided.
*/
static final <T, U> Converter<T, U> converter(Configuration configuration, T instance, Class<T> tType, Class<U> uType) {
static final <T, U> ScopedConverter<T, U> converter(Configuration configuration, T instance, Class<T> tType, Class<U> uType) {
Converter<T, U> result = configuration(configuration).converterProvider().provide(tType, uType);
if (result == null)
@ -1479,15 +1477,15 @@ final class Tools {
if (result == null && tType == Converters.UnknownType.class)
result = converter(configuration, instance, (Class<T>) (instance == null ? Object.class : instance.getClass()), uType);
return result;
return result == null ? null : scoped(result);
}
/**
* Get a converter from a {@link ConverterProvider} or <code>null</code> if
* no converter could be provided.
*/
static final <T, U> Converter<T, U> converterOrFail(Configuration configuration, T instance, Class<T> tType, Class<U> uType) {
Converter<T, U> result = converter(configuration, instance, tType, uType);
static final <T, U> ScopedConverter<T, U> converterOrFail(Configuration configuration, T instance, Class<T> tType, Class<U> uType) {
ScopedConverter<T, U> result = converter(configuration, instance, tType, uType);
if (result == null)
throw new DataTypeException("No Converter found for types " + tType.getName() + " and " + uType.getName());
@ -1498,7 +1496,7 @@ final class Tools {
/**
* Get a converter from a {@link ConverterProvider}.
*/
static final <T, U> Converter<T, U> converterOrFail(Attachable attachable, T instance, Class<T> tType, Class<U> uType) {
static final <T, U> ScopedConverter<T, U> converterOrFail(Attachable attachable, T instance, Class<T> tType, Class<U> uType) {
return converterOrFail(configuration(attachable), instance, tType, uType);
}
@ -7105,4 +7103,12 @@ final class Tools {
return dataType;
}
static final ConverterScope converterScope(Attachable attachable) {
return new DefaultConverterScope(configuration(attachable));
}
static final ConverterScope converterScope(Configuration configuration) {
return new DefaultConverterScope(configuration(configuration));
}
}

View File

@ -40,6 +40,7 @@ package org.jooq.impl;
import java.io.StringReader;
import java.io.StringWriter;
import org.jooq.ConverterScope;
import org.jooq.XML;
import jakarta.xml.bind.JAXB;
@ -56,21 +57,21 @@ public class XMLtoJAXBConverter<U> extends AbstractConverter<XML, U> {
}
@Override
public U from(XML databaseObject) {
if (databaseObject == null)
public U from(XML t) {
if (t == null)
return null;
else
return JAXB.unmarshal(new StringReader(databaseObject.data()), toType());
return JAXB.unmarshal(new StringReader(t.data()), toType());
}
@Override
public XML to(U userObject) {
if (userObject == null) {
public XML to(U u) {
if (u == null) {
return null;
}
else {
StringWriter w = new StringWriter();
JAXB.marshal(userObject, w);
JAXB.marshal(u, w);
return XML.xml(w.toString());
}
}

View File

@ -40,7 +40,9 @@ package org.jooq.tools;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.MILLI_OF_DAY;
import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
import static org.jooq.ScopedConverter.scoped;
import static org.jooq.impl.Internal.arrayType;
import static org.jooq.impl.Internal.converterScope;
import static org.jooq.tools.reflect.Reflect.accessible;
import static org.jooq.tools.reflect.Reflect.wrapper;
import static org.jooq.types.Unsigned.ubyte;
@ -91,6 +93,7 @@ import jakarta.xml.bind.JAXB;
// ...
import org.jooq.Converter;
import org.jooq.ConverterProvider;
import org.jooq.ConverterScope;
import org.jooq.DataType;
import org.jooq.EnumType;
import org.jooq.Field;
@ -100,9 +103,13 @@ import org.jooq.QualifiedRecord;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.SQLDialect;
import org.jooq.ScopedConverter;
import org.jooq.XML;
import org.jooq.exception.DataTypeException;
import org.jooq.impl.AbstractConverter;
import org.jooq.impl.AbstractScopedConverter;
import org.jooq.impl.IdentityConverter;
import org.jooq.impl.Internal;
import org.jooq.tools.jdbc.MockArray;
import org.jooq.tools.jdbc.MockResultSet;
import org.jooq.tools.reflect.Reflect;
@ -370,7 +377,7 @@ public final class Convert {
}
public static final <U> U[] convertCollection(Collection from, Class<? extends U[]> to){
return new ConvertAll<U[]>(to).from(from);
return new ConvertAll<U[]>(to).from(from, converterScope());
}
/**
@ -400,10 +407,10 @@ public final class Convert {
Class<T> fromType = converter.fromType();
if (fromType == Object.class)
return converter.from((T) from);
return scoped(converter).from((T) from, converterScope());
ConvertAll<T> convertAll = new ConvertAll<>(fromType);
return converter.from(convertAll.from(from));
return scoped(converter).from(convertAll.from(from, converterScope()), converterScope());
}
/**
@ -514,7 +521,7 @@ public final class Convert {
List<U> result = new ArrayList<>(collection.size());
for (Object o : collection)
result.add(convert(all.from(o), converter));
result.add(convert(all.from(o, converterScope()), converter));
return result;
}
@ -527,17 +534,20 @@ public final class Convert {
/**
* The converter to convert them all.
*/
private static class ConvertAll<U> implements Converter<Object, U> {
private static final class ConvertAll<U> extends AbstractScopedConverter<Object, U> {
private final Class<? extends U> toClass;
@SuppressWarnings("unchecked")
ConvertAll(Class<? extends U> toClass) {
super(Object.class, (Class<U>) toClass);
this.toClass = toClass;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public U from(Object from) {
public U from(Object from, ConverterScope scope) {
if (from == null) {
// [#936] If types are converted to primitives, the result must not
@ -1239,22 +1249,10 @@ public final class Convert {
}
@Override
public Object to(U to) {
public Object to(U to, ConverterScope scope) {
return to;
}
@Override
public Class<Object> fromType() {
return Object.class;
}
@SuppressWarnings("unchecked")
@Override
public Class<U> toType() {
return (Class<U>) toClass;
}
/**
* Convert a long timestamp (millis) to any date type.
*/

View File

@ -38,6 +38,8 @@
package org.jooq.tools.jdbc;
import static org.jooq.SQLDialect.DEFAULT;
import static org.jooq.ScopedConverter.scoped;
import static org.jooq.impl.Internal.converterScope;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@ -71,8 +73,10 @@ import org.jooq.Converters;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.ScopedConverter;
import org.jooq.impl.DSL;
import org.jooq.impl.DefaultConfiguration;
import org.jooq.impl.Internal;
import org.jooq.tools.StringUtils;
/**
@ -457,7 +461,7 @@ public class MockResultSet extends JDBC41ResultSet implements ResultSet, Seriali
.converterProvider()
.provide(value == null ? Object.class : (Class<Object>) value.getClass(), type);
T converted = converter == null ? null : converter.from(value);
T converted = converter == null ? null : scoped(converter).from(value, converterScope());
wasNull = (converted == null);
return converted;
}

View File

@ -38,6 +38,7 @@
package org.jooq.util.postgres;
import static java.lang.Integer.toOctalString;
import static org.jooq.impl.Internal.converterScope;
import static org.jooq.tools.StringUtils.leftPad;
import java.io.ByteArrayOutputStream;
@ -52,6 +53,7 @@ import java.util.List;
import org.jooq.Converter;
import org.jooq.EnumType;
import org.jooq.Record;
import org.jooq.ScopedConverter;
import org.jooq.exception.DataTypeException;
import org.jooq.tools.StringUtils;
import org.jooq.types.DayToSecond;
@ -529,7 +531,7 @@ public class PostgresUtils {
String separator = "";
for (int i = 0; i < r.size(); i++) {
@SuppressWarnings({ "unchecked", "rawtypes" })
Object a = ((Converter) r.field(i).getConverter()).to(r.get(i));
Object a = ((ScopedConverter) r.field(i).getConverter()).to(r.get(i), converterScope());
sb.append(separator);
// [#753] null must not be set as a literal