diff --git a/jOOQ/src/main/java/org/jooq/Binding.java b/jOOQ/src/main/java/org/jooq/Binding.java index 092d1efeeb..71eaefb828 100644 --- a/jOOQ/src/main/java/org/jooq/Binding.java +++ b/jOOQ/src/main/java/org/jooq/Binding.java @@ -51,6 +51,7 @@ import java.sql.Timestamp; import org.jooq.exception.DataAccessException; import org.jooq.impl.DefaultBinding; +import org.jooq.impl.SQLDataType; /** * An SPI (Service Provider Interface) that exposes all low-level interactions @@ -61,9 +62,18 @@ import org.jooq.impl.DefaultBinding; * internal support for bind variable types is implemented in * {@link DefaultBinding}. * + * @param The database type - i.e. any type available from + * {@link SQLDataType} + * @param The user type * @author Lukas Eder */ -public interface Binding extends Serializable { +public interface Binding extends Serializable { + + /** + * A converter that can convert between the database type and the custom + * type. + */ + Converter converter(); /** * Generate SQL code for the bind variable. @@ -108,7 +118,7 @@ public interface Binding extends Serializable { * {@link SQLException}s to the caller to be wrapped in * {@link DataAccessException}s. */ - void sql(BindingSQLContext ctx) throws SQLException; + void sql(BindingSQLContext ctx) throws SQLException; /** * Register a {@link CallableStatement}'s OUT parameter. @@ -118,7 +128,7 @@ public interface Binding extends Serializable { * {@link SQLException}s to the caller to be wrapped in * {@link DataAccessException}s. */ - void register(BindingRegisterContext ctx) throws SQLException; + void register(BindingRegisterContext ctx) throws SQLException; /** * Set a {@link PreparedStatement}'s IN parameter. @@ -128,7 +138,7 @@ public interface Binding extends Serializable { * {@link SQLException}s to the caller to be wrapped in * {@link DataAccessException}s. */ - void set(BindingSetStatementContext ctx) throws SQLException; + void set(BindingSetStatementContext ctx) throws SQLException; /** * Set a {@link SQLOutput}'s IN parameter. @@ -138,7 +148,7 @@ public interface Binding extends Serializable { * {@link SQLException}s to the caller to be wrapped in * {@link DataAccessException}s. */ - void set(BindingSetSQLOutputContext ctx) throws SQLException; + void set(BindingSetSQLOutputContext ctx) throws SQLException; /** * Get a {@link ResultSet}'s OUT value. @@ -152,7 +162,7 @@ public interface Binding extends Serializable { * {@link SQLException}s to the caller to be wrapped in * {@link DataAccessException}s. */ - void get(BindingGetResultSetContext ctx) throws SQLException; + void get(BindingGetResultSetContext ctx) throws SQLException; /** * Get a {@link CallableStatement}'s OUT value. @@ -166,7 +176,7 @@ public interface Binding extends Serializable { * {@link SQLException}s to the caller to be wrapped in * {@link DataAccessException}s. */ - void get(BindingGetStatementContext ctx) throws SQLException; + void get(BindingGetStatementContext ctx) throws SQLException; /** * Get a {@link SQLInput}'s OUT value. @@ -180,5 +190,5 @@ public interface Binding extends Serializable { * {@link SQLException}s to the caller to be wrapped in * {@link DataAccessException}s. */ - void get(BindingGetSQLInputContext ctx) throws SQLException; + void get(BindingGetSQLInputContext ctx) throws SQLException; } diff --git a/jOOQ/src/main/java/org/jooq/DataType.java b/jOOQ/src/main/java/org/jooq/DataType.java index 31f0bacbad..2d23722679 100644 --- a/jOOQ/src/main/java/org/jooq/DataType.java +++ b/jOOQ/src/main/java/org/jooq/DataType.java @@ -106,6 +106,11 @@ public interface DataType extends Serializable { */ DataType asConvertedDataType(Converter converter); + /** + * Retrieve the data type for a given converter. + */ + DataType asConvertedDataType(Binding converter); + /** * Retrieve the dialect-specific type name associated with this data type. */ diff --git a/jOOQ/src/main/java/org/jooq/Field.java b/jOOQ/src/main/java/org/jooq/Field.java index 58c9037d0f..d06188adb9 100644 --- a/jOOQ/src/main/java/org/jooq/Field.java +++ b/jOOQ/src/main/java/org/jooq/Field.java @@ -119,7 +119,7 @@ public interface Field extends GroupField { /** * The field's underlying {@link Binding}. */ - Binding getBinding(); + Binding getBinding(); /** * The Java type of the field. diff --git a/jOOQ/src/main/java/org/jooq/Parameter.java b/jOOQ/src/main/java/org/jooq/Parameter.java index 126301224c..b96766c87d 100644 --- a/jOOQ/src/main/java/org/jooq/Parameter.java +++ b/jOOQ/src/main/java/org/jooq/Parameter.java @@ -72,7 +72,7 @@ public interface Parameter extends QueryPart { /** * The parameter's underlying {@link Binding}. */ - Binding getBinding(); + Binding getBinding(); /** * The type of this parameter (might not be dialect-specific) diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 055cf5d8f9..167711bbbd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -113,28 +113,25 @@ abstract class AbstractField extends AbstractQueryPart implements Field { private final String name; private final String comment; private final DataType dataType; - private final Converter converter; - private final Binding binding; + private final Binding binding; AbstractField(String name, DataType type) { this(name, type, null, null); } - @SuppressWarnings("unchecked") - AbstractField(String name, DataType type, String comment, Converter converter) { + AbstractField(String name, DataType type, String comment, Binding binding) { super(); this.name = name; this.comment = defaultString(comment); this.dataType = type; - this.converter = - converter != null - ? converter - : type instanceof ConvertedDataType - ? ((ConvertedDataType) type).converter() - : new IdentityConverter(type.getType()); - this.binding = new DefaultBinding(this.converter, type.isLob()); + this.binding = + binding != null + ? binding + : type instanceof ConvertedDataType + ? ((ConvertedDataType) type).binding() + : new DefaultBinding(new IdentityConverter(type.getType())); } // ------------------------------------------------------------------------ @@ -175,11 +172,11 @@ abstract class AbstractField extends AbstractQueryPart implements Field { @Override public final Converter getConverter() { - return converter; + return binding.converter(); } @Override - public final Binding getBinding() { + public final Binding getBinding() { return binding; } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index 5d337d5db6..0a16fd0ac2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -67,6 +67,7 @@ import java.util.Set; import org.jooq.AggregateFunction; import org.jooq.AttachableInternal; import org.jooq.BindContext; +import org.jooq.Binding; import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Context; @@ -752,7 +753,7 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro * @param type The data type of the field */ protected static final Parameter createParameter(String name, DataType type) { - return createParameter(name, type, false, null); + return createParameter(name, type, false, (Binding) null); } /** @@ -765,7 +766,7 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro * {@link Parameter#isDefaulted()} */ protected static final Parameter createParameter(String name, DataType type, boolean isDefaulted) { - return createParameter(name, type, isDefaulted, null); + return createParameter(name, type, isDefaulted, (Binding) null); } /** @@ -778,7 +779,25 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro * {@link Parameter#isDefaulted()} */ protected static final Parameter createParameter(String name, DataType type, boolean isDefaulted, Converter converter) { - return new ParameterImpl(name, type, isDefaulted, converter); + return createParameter(name, type, isDefaulted, new DefaultBinding(converter, type.isLob())); + } + + /** + * Subclasses may call this method to create {@link UDTField} objects that + * are linked to this table. + * + * @param name The name of the field (case-sensitive!) + * @param type The data type of the field + * @param isDefaulted Whether the parameter is defaulted (see + * {@link Parameter#isDefaulted()} + */ + @SuppressWarnings("unchecked") + protected static final Parameter createParameter(String name, DataType type, boolean isDefaulted, Binding binding) { + final DataType actualType = binding == null + ? (DataType) type + : type.asConvertedDataType(binding); + + return new ParameterImpl(name, actualType, isDefaulted, binding); } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index 9e3345ef73..2f3e0b2595 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -60,6 +60,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import org.jooq.Binding; import org.jooq.Clause; import org.jooq.Context; import org.jooq.Converter; @@ -320,7 +321,7 @@ abstract class AbstractTable extends AbstractQueryPart impleme * @param type The data type of the field */ protected static final TableField createField(String name, DataType type, Table table) { - return createField(name, type, table, null); + return createField(name, type, table, null, (Binding) null); } /** @@ -331,7 +332,18 @@ abstract class AbstractTable extends AbstractQueryPart impleme * @param type The data type of the field */ protected static final TableField createField(String name, DataType type, Table table, String comment) { - return createField(name, type, table, comment, null); + return createField(name, type, table, comment, (Binding) null); + } + + /** + * Subclasses may call this method to create {@link TableField} objects that + * are linked to this table. + * + * @param name The name of the field (case-sensitive!) + * @param type The data type of the field + */ + protected static final TableField createField(String name, DataType type, Table table, String comment, Converter converter) { + return createField(name, type, table, comment, converter == null ? null : new DefaultBinding(converter, type.isLob())); } /** @@ -342,12 +354,12 @@ abstract class AbstractTable extends AbstractQueryPart impleme * @param type The data type of the field */ @SuppressWarnings("unchecked") - protected static final TableField createField(String name, DataType type, Table table, String comment, Converter converter) { - final DataType actualType = converter == null + protected static final TableField createField(String name, DataType type, Table table, String comment, Binding binding) { + final DataType actualType = binding == null ? (DataType) type - : type.asConvertedDataType(converter); + : type.asConvertedDataType(binding); - final TableFieldImpl tableField = new TableFieldImpl(name, actualType, table, comment, converter); + final TableFieldImpl tableField = new TableFieldImpl(name, actualType, table, comment, binding); // [#1199] The public API of Table returns immutable field lists if (table instanceof TableImpl) { diff --git a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java index 5f30e0846e..c0d95aebab 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java @@ -40,6 +40,7 @@ */ package org.jooq.impl; +import org.jooq.Binding; import org.jooq.Configuration; import org.jooq.Converter; import org.jooq.DataType; @@ -54,15 +55,15 @@ class ConvertedDataType extends DefaultDataType { /** * Generated UID */ - private static final long serialVersionUID = -2321926692580974126L; + private static final long serialVersionUID = -2321926692580974126L; - private final DataType delegate; - private final Converter converter; + private final DataType delegate; + private final Binding binding; - ConvertedDataType(DataType delegate, Converter converter) { + ConvertedDataType(DataType delegate, Binding binding) { super( null, - converter.toType(), + binding.converter().toType(), delegate.getTypeName(), delegate.getCastTypeName(), delegate.precision(), @@ -73,7 +74,7 @@ class ConvertedDataType extends DefaultDataType { ); this.delegate = delegate; - this.converter = converter; + this.binding = binding; } @Override @@ -94,17 +95,21 @@ class ConvertedDataType extends DefaultDataType { @SuppressWarnings("unchecked") @Override public U convert(Object object) { - if (converter.toType().isInstance(object)) { + if (binding.converter().toType().isInstance(object)) { return (U) object; } // [#3200] Try to convert arbitrary objects to T else { - return converter.from(delegate.convert(object)); + return binding.converter().from(delegate.convert(object)); } } + Binding binding() { + return binding; + } + Converter converter() { - return converter; + return binding.converter(); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index 0842d5984a..9e61fb45fb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -134,7 +134,7 @@ import org.jooq.util.postgres.PostgresUtils; /** * @author Lukas Eder */ -public class DefaultBinding implements Binding { +public class DefaultBinding implements Binding { static final JooqLogger log = JooqLogger.getLogger(DefaultBinding.class); private static final char[] HEX = "0123456789abcdef".toCharArray(); @@ -169,6 +169,11 @@ public class DefaultBinding implements Binding { this.paramName = paramName; } + @Override + public Converter converter() { + return converter; + } + @Override public void sql(BindingSQLContext ctx) { T converted = converter.to(ctx.value()); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java index 53484dd754..587b9415c3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java @@ -62,6 +62,7 @@ import java.util.Map; import java.util.regex.Pattern; // ... +import org.jooq.Binding; import org.jooq.Configuration; import org.jooq.Converter; import org.jooq.DataType; @@ -586,7 +587,12 @@ public class DefaultDataType implements DataType { @Override public final DataType asConvertedDataType(Converter converter) { - return new ConvertedDataType(this, converter); + return asConvertedDataType(new DefaultBinding((Converter) converter, isLob())); + } + + @Override + public final DataType asConvertedDataType(Binding binding) { + return new ConvertedDataType(this, binding); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java index f7b448aeb2..405691eb91 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java @@ -61,26 +61,21 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { private final String name; private final DataType type; - private final Converter converter; - private final Binding binding; + private final Binding binding; private final boolean isDefaulted; - @SuppressWarnings({ "unchecked", "rawtypes" }) - ParameterImpl(String name, DataType type, boolean isDefaulted, Converter converter) { + @SuppressWarnings({ "unchecked" }) + ParameterImpl(String name, DataType type, boolean isDefaulted, Binding binding) { this.name = name; this.isDefaulted = isDefaulted; - this.type = converter == null - ? (DataType) type - : type.asConvertedDataType((Converter) converter); + this.type = type; - this.converter = - converter != null - ? converter + this.binding = + binding != null + ? binding : type instanceof ConvertedDataType - ? ((ConvertedDataType) type).converter() - : new IdentityConverter((Class) type.getType()); - - this.binding = new DefaultBinding(this.converter, type.isLob()); + ? ((ConvertedDataType) type).binding() + : new DefaultBinding(new IdentityConverter(type.getType())); } @Override @@ -90,11 +85,11 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { @Override public final Converter getConverter() { - return converter; + return binding.converter(); } @Override - public final Binding getBinding() { + public final Binding getBinding() { return binding; } diff --git a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java index 33381736d8..0c25673514 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java @@ -98,7 +98,7 @@ class TableAlias extends AbstractTable { name = fieldAliases[i]; } - result.add(new TableFieldImpl(name, field.getDataType(), this, field.getComment(), field.getConverter())); + result.add(new TableFieldImpl(name, field.getDataType(), this, field.getComment(), field.getBinding())); } return new Fields(result); diff --git a/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java index e63cae927f..cc73f741c1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java @@ -45,9 +45,9 @@ import static org.jooq.Clause.FIELD; import static org.jooq.Clause.FIELD_REFERENCE; import static org.jooq.impl.Utils.DATA_OMIT_CLAUSE_EVENT_EMISSION; +import org.jooq.Binding; import org.jooq.Clause; import org.jooq.Context; -import org.jooq.Converter; import org.jooq.DataType; import org.jooq.Record; import org.jooq.Table; @@ -66,8 +66,8 @@ class TableFieldImpl extends AbstractField implements Ta private final Table table; - TableFieldImpl(String name, DataType type, Table table, String comment, Converter converter) { - super(name, type, comment, converter); + TableFieldImpl(String name, DataType type, Table table, String comment, Binding binding) { + super(name, type, comment, binding); this.table = table; } diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java index df6e5288d8..94b92b1c31 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java @@ -41,8 +41,8 @@ package org.jooq.impl; +import org.jooq.Binding; import org.jooq.Context; -import org.jooq.Converter; import org.jooq.DataType; import org.jooq.UDT; import org.jooq.UDTField; @@ -59,8 +59,8 @@ class UDTFieldImpl, T> extends AbstractField implement private final UDT udt; - UDTFieldImpl(String name, DataType type, UDT udt, String comment, Converter converter) { - super(name, type, comment, converter); + UDTFieldImpl(String name, DataType type, UDT udt, String comment, Binding binding) { + super(name, type, comment, binding); this.udt = udt; diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java index 515893781b..3a49c12f0d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java @@ -40,6 +40,7 @@ */ package org.jooq.impl; +import org.jooq.Binding; import org.jooq.Clause; import org.jooq.Context; import org.jooq.Converter; @@ -162,7 +163,7 @@ public class UDTImpl> extends AbstractQueryPart implement * @param type The data type of the field */ protected static final , T> UDTField createField(String name, DataType type, UDT udt) { - return createField(name, type, udt, "", null); + return createField(name, type, udt, "", (Binding) null); } /** @@ -173,7 +174,18 @@ public class UDTImpl> extends AbstractQueryPart implement * @param type The data type of the field */ protected static final , T> UDTField createField(String name, DataType type, UDT udt, String comment) { - return createField(name, type, udt, comment, null); + return createField(name, type, udt, comment, (Binding) null); + } + + /** + * Subclasses may call this method to create {@link UDTField} objects that + * are linked to this table. + * + * @param name The name of the field (case-sensitive!) + * @param type The data type of the field + */ + protected static final , T, U> UDTField createField(String name, DataType type, UDT udt, String comment, Converter converter) { + return createField(name, type, udt, comment, converter == null ? null : new DefaultBinding(converter, type.isLob())); } /** @@ -184,12 +196,12 @@ public class UDTImpl> extends AbstractQueryPart implement * @param type The data type of the field */ @SuppressWarnings("unchecked") - protected static final , T, U> UDTField createField(String name, DataType type, UDT udt, String comment, Converter converter) { - final DataType actualType = converter == null + protected static final , T, U> UDTField createField(String name, DataType type, UDT udt, String comment, Binding binding) { + final DataType actualType = binding == null ? (DataType) type - : type.asConvertedDataType(converter); + : type.asConvertedDataType(binding); - final UDTFieldImpl udtField = new UDTFieldImpl(name, actualType, udt, comment, converter); + final UDTFieldImpl udtField = new UDTFieldImpl(name, actualType, udt, comment, binding); return udtField; }