[#3248] Binding<T, U> should have two type parameters

This commit is contained in:
Lukas Eder 2014-11-12 18:33:49 +01:00
parent 7aaa4774d0
commit 3897a78213
15 changed files with 138 additions and 72 deletions

View File

@ -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 <T> The database type - i.e. any type available from
* {@link SQLDataType}
* @param <U> The user type
* @author Lukas Eder
*/
public interface Binding<T> extends Serializable {
public interface Binding<T, U> extends Serializable {
/**
* A converter that can convert between the database type and the custom
* type.
*/
Converter<T, U> converter();
/**
* Generate SQL code for the bind variable.
@ -108,7 +118,7 @@ public interface Binding<T> extends Serializable {
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void sql(BindingSQLContext<T> ctx) throws SQLException;
void sql(BindingSQLContext<U> ctx) throws SQLException;
/**
* Register a {@link CallableStatement}'s <code>OUT</code> parameter.
@ -118,7 +128,7 @@ public interface Binding<T> extends Serializable {
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void register(BindingRegisterContext<T> ctx) throws SQLException;
void register(BindingRegisterContext<U> ctx) throws SQLException;
/**
* Set a {@link PreparedStatement}'s <code>IN</code> parameter.
@ -128,7 +138,7 @@ public interface Binding<T> extends Serializable {
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void set(BindingSetStatementContext<T> ctx) throws SQLException;
void set(BindingSetStatementContext<U> ctx) throws SQLException;
/**
* Set a {@link SQLOutput}'s <code>IN</code> parameter.
@ -138,7 +148,7 @@ public interface Binding<T> extends Serializable {
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void set(BindingSetSQLOutputContext<T> ctx) throws SQLException;
void set(BindingSetSQLOutputContext<U> ctx) throws SQLException;
/**
* Get a {@link ResultSet}'s <code>OUT</code> value.
@ -152,7 +162,7 @@ public interface Binding<T> extends Serializable {
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void get(BindingGetResultSetContext<T> ctx) throws SQLException;
void get(BindingGetResultSetContext<U> ctx) throws SQLException;
/**
* Get a {@link CallableStatement}'s <code>OUT</code> value.
@ -166,7 +176,7 @@ public interface Binding<T> extends Serializable {
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void get(BindingGetStatementContext<T> ctx) throws SQLException;
void get(BindingGetStatementContext<U> ctx) throws SQLException;
/**
* Get a {@link SQLInput}'s <code>OUT</code> value.
@ -180,5 +190,5 @@ public interface Binding<T> extends Serializable {
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void get(BindingGetSQLInputContext<T> ctx) throws SQLException;
void get(BindingGetSQLInputContext<U> ctx) throws SQLException;
}

View File

@ -106,6 +106,11 @@ public interface DataType<T> extends Serializable {
*/
<U> DataType<U> asConvertedDataType(Converter<? super T, U> converter);
/**
* Retrieve the data type for a given converter.
*/
<U> DataType<U> asConvertedDataType(Binding<? super T, U> converter);
/**
* Retrieve the dialect-specific type name associated with this data type.
*/

View File

@ -119,7 +119,7 @@ public interface Field<T> extends GroupField {
/**
* The field's underlying {@link Binding}.
*/
Binding<T> getBinding();
Binding<?, T> getBinding();
/**
* The Java type of the field.

View File

@ -72,7 +72,7 @@ public interface Parameter<T> extends QueryPart {
/**
* The parameter's underlying {@link Binding}.
*/
Binding<T> getBinding();
Binding<?, T> getBinding();
/**
* The type of this parameter (might not be dialect-specific)

View File

@ -113,28 +113,25 @@ abstract class AbstractField<T> extends AbstractQueryPart implements Field<T> {
private final String name;
private final String comment;
private final DataType<T> dataType;
private final Converter<?, T> converter;
private final Binding<T> binding;
private final Binding<?, T> binding;
AbstractField(String name, DataType<T> type) {
this(name, type, null, null);
}
@SuppressWarnings("unchecked")
AbstractField(String name, DataType<T> type, String comment, Converter<?, T> converter) {
AbstractField(String name, DataType<T> type, String comment, Binding<?, T> binding) {
super();
this.name = name;
this.comment = defaultString(comment);
this.dataType = type;
this.converter =
converter != null
? converter
: type instanceof ConvertedDataType
? ((ConvertedDataType<?, T>) type).converter()
: new IdentityConverter<T>(type.getType());
this.binding = new DefaultBinding(this.converter, type.isLob());
this.binding =
binding != null
? binding
: type instanceof ConvertedDataType
? ((ConvertedDataType<?, T>) type).binding()
: new DefaultBinding<T, T>(new IdentityConverter<T>(type.getType()));
}
// ------------------------------------------------------------------------
@ -175,11 +172,11 @@ abstract class AbstractField<T> extends AbstractQueryPart implements Field<T> {
@Override
public final Converter<?, T> getConverter() {
return converter;
return binding.converter();
}
@Override
public final Binding<T> getBinding() {
public final Binding<?, T> getBinding() {
return binding;
}

View File

@ -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<T> extends AbstractQueryPart implements Ro
* @param type The data type of the field
*/
protected static final <T> Parameter<T> createParameter(String name, DataType<T> type) {
return createParameter(name, type, false, null);
return createParameter(name, type, false, (Binding<T, T>) null);
}
/**
@ -765,7 +766,7 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
* {@link Parameter#isDefaulted()}
*/
protected static final <T> Parameter<T> createParameter(String name, DataType<T> type, boolean isDefaulted) {
return createParameter(name, type, isDefaulted, null);
return createParameter(name, type, isDefaulted, (Binding<T, T>) null);
}
/**
@ -778,7 +779,25 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
* {@link Parameter#isDefaulted()}
*/
protected static final <T, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, Converter<T, U> converter) {
return new ParameterImpl<U>(name, type, isDefaulted, converter);
return createParameter(name, type, isDefaulted, new DefaultBinding<T, U>(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 <T, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, Binding<T, U> binding) {
final DataType<U> actualType = binding == null
? (DataType<U>) type
: type.asConvertedDataType(binding);
return new ParameterImpl<U>(name, actualType, isDefaulted, binding);
}
/**

View File

@ -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<R extends Record> extends AbstractQueryPart impleme
* @param type The data type of the field
*/
protected static final <R extends Record, T> TableField<R, T> createField(String name, DataType<T> type, Table<R> table) {
return createField(name, type, table, null);
return createField(name, type, table, null, (Binding<T, T>) null);
}
/**
@ -331,7 +332,18 @@ abstract class AbstractTable<R extends Record> extends AbstractQueryPart impleme
* @param type The data type of the field
*/
protected static final <R extends Record, T> TableField<R, T> createField(String name, DataType<T> type, Table<R> table, String comment) {
return createField(name, type, table, comment, null);
return createField(name, type, table, comment, (Binding<T, T>) 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 <R extends Record, T, U> TableField<R, U> createField(String name, DataType<T> type, Table<R> table, String comment, Converter<T, U> converter) {
return createField(name, type, table, comment, converter == null ? null : new DefaultBinding<T, U>(converter, type.isLob()));
}
/**
@ -342,12 +354,12 @@ abstract class AbstractTable<R extends Record> extends AbstractQueryPart impleme
* @param type The data type of the field
*/
@SuppressWarnings("unchecked")
protected static final <R extends Record, T, U> TableField<R, U> createField(String name, DataType<T> type, Table<R> table, String comment, Converter<T, U> converter) {
final DataType<U> actualType = converter == null
protected static final <R extends Record, T, U> TableField<R, U> createField(String name, DataType<T> type, Table<R> table, String comment, Binding<T, U> binding) {
final DataType<U> actualType = binding == null
? (DataType<U>) type
: type.asConvertedDataType(converter);
: type.asConvertedDataType(binding);
final TableFieldImpl<R, U> tableField = new TableFieldImpl<R, U>(name, actualType, table, comment, converter);
final TableFieldImpl<R, U> tableField = new TableFieldImpl<R, U>(name, actualType, table, comment, binding);
// [#1199] The public API of Table returns immutable field lists
if (table instanceof TableImpl) {

View File

@ -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<T, U> extends DefaultDataType<U> {
/**
* Generated UID
*/
private static final long serialVersionUID = -2321926692580974126L;
private static final long serialVersionUID = -2321926692580974126L;
private final DataType<T> delegate;
private final Converter<? super T, U> converter;
private final DataType<T> delegate;
private final Binding<? super T, U> binding;
ConvertedDataType(DataType<T> delegate, Converter<? super T, U> converter) {
ConvertedDataType(DataType<T> delegate, Binding<? super T, U> binding) {
super(
null,
converter.toType(),
binding.converter().toType(),
delegate.getTypeName(),
delegate.getCastTypeName(),
delegate.precision(),
@ -73,7 +74,7 @@ class ConvertedDataType<T, U> extends DefaultDataType<U> {
);
this.delegate = delegate;
this.converter = converter;
this.binding = binding;
}
@Override
@ -94,17 +95,21 @@ class ConvertedDataType<T, U> extends DefaultDataType<U> {
@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<? super T, U> binding() {
return binding;
}
Converter<? super T, U> converter() {
return converter;
return binding.converter();
}
}

View File

@ -134,7 +134,7 @@ import org.jooq.util.postgres.PostgresUtils;
/**
* @author Lukas Eder
*/
public class DefaultBinding<T, U> implements Binding<U> {
public class DefaultBinding<T, U> implements Binding<T, U> {
static final JooqLogger log = JooqLogger.getLogger(DefaultBinding.class);
private static final char[] HEX = "0123456789abcdef".toCharArray();
@ -169,6 +169,11 @@ public class DefaultBinding<T, U> implements Binding<U> {
this.paramName = paramName;
}
@Override
public Converter<T, U> converter() {
return converter;
}
@Override
public void sql(BindingSQLContext<U> ctx) {
T converted = converter.to(ctx.value());

View File

@ -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<T> implements DataType<T> {
@Override
public final <U> DataType<U> asConvertedDataType(Converter<? super T, U> converter) {
return new ConvertedDataType<T, U>(this, converter);
return asConvertedDataType(new DefaultBinding<T, U>((Converter) converter, isLob()));
}
@Override
public final <U> DataType<U> asConvertedDataType(Binding<? super T, U> binding) {
return new ConvertedDataType<T, U>(this, binding);
}
@Override

View File

@ -61,26 +61,21 @@ class ParameterImpl<T> extends AbstractQueryPart implements Parameter<T> {
private final String name;
private final DataType<T> type;
private final Converter<?, T> converter;
private final Binding<T> binding;
private final Binding<?, T> binding;
private final boolean isDefaulted;
@SuppressWarnings({ "unchecked", "rawtypes" })
ParameterImpl(String name, DataType<?> type, boolean isDefaulted, Converter<?, T> converter) {
@SuppressWarnings({ "unchecked" })
ParameterImpl(String name, DataType<T> type, boolean isDefaulted, Binding<?, T> binding) {
this.name = name;
this.isDefaulted = isDefaulted;
this.type = converter == null
? (DataType<T>) type
: type.asConvertedDataType((Converter) converter);
this.type = type;
this.converter =
converter != null
? converter
this.binding =
binding != null
? binding
: type instanceof ConvertedDataType
? ((ConvertedDataType<?, T>) type).converter()
: new IdentityConverter<T>((Class<T>) type.getType());
this.binding = new DefaultBinding(this.converter, type.isLob());
? ((ConvertedDataType<?, T>) type).binding()
: new DefaultBinding<T, T>(new IdentityConverter<T>(type.getType()));
}
@Override
@ -90,11 +85,11 @@ class ParameterImpl<T> extends AbstractQueryPart implements Parameter<T> {
@Override
public final Converter<?, T> getConverter() {
return converter;
return binding.converter();
}
@Override
public final Binding<T> getBinding() {
public final Binding<?, T> getBinding() {
return binding;
}

View File

@ -98,7 +98,7 @@ class TableAlias<R extends Record> extends AbstractTable<R> {
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<R>(result);

View File

@ -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<R extends Record, T> extends AbstractField<T> implements Ta
private final Table<R> table;
TableFieldImpl(String name, DataType<T> type, Table<R> table, String comment, Converter<?, T> converter) {
super(name, type, comment, converter);
TableFieldImpl(String name, DataType<T> type, Table<R> table, String comment, Binding<?, T> binding) {
super(name, type, comment, binding);
this.table = table;
}

View File

@ -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<R extends UDTRecord<R>, T> extends AbstractField<T> implement
private final UDT<R> udt;
UDTFieldImpl(String name, DataType<T> type, UDT<R> udt, String comment, Converter<?, T> converter) {
super(name, type, comment, converter);
UDTFieldImpl(String name, DataType<T> type, UDT<R> udt, String comment, Binding<?, T> binding) {
super(name, type, comment, binding);
this.udt = udt;

View File

@ -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<R extends UDTRecord<R>> extends AbstractQueryPart implement
* @param type The data type of the field
*/
protected static final <R extends UDTRecord<R>, T> UDTField<R, T> createField(String name, DataType<T> type, UDT<R> udt) {
return createField(name, type, udt, "", null);
return createField(name, type, udt, "", (Binding<T, T>) null);
}
/**
@ -173,7 +174,18 @@ public class UDTImpl<R extends UDTRecord<R>> extends AbstractQueryPart implement
* @param type The data type of the field
*/
protected static final <R extends UDTRecord<R>, T> UDTField<R, T> createField(String name, DataType<T> type, UDT<R> udt, String comment) {
return createField(name, type, udt, comment, null);
return createField(name, type, udt, comment, (Binding<T, T>) 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 <R extends UDTRecord<R>, T, U> UDTField<R, U> createField(String name, DataType<T> type, UDT<R> udt, String comment, Converter<T, U> converter) {
return createField(name, type, udt, comment, converter == null ? null : new DefaultBinding<T, U>(converter, type.isLob()));
}
/**
@ -184,12 +196,12 @@ public class UDTImpl<R extends UDTRecord<R>> extends AbstractQueryPart implement
* @param type The data type of the field
*/
@SuppressWarnings("unchecked")
protected static final <R extends UDTRecord<R>, T, U> UDTField<R, U> createField(String name, DataType<T> type, UDT<R> udt, String comment, Converter<T, U> converter) {
final DataType<U> actualType = converter == null
protected static final <R extends UDTRecord<R>, T, U> UDTField<R, U> createField(String name, DataType<T> type, UDT<R> udt, String comment, Binding<T, U> binding) {
final DataType<U> actualType = binding == null
? (DataType<U>) type
: type.asConvertedDataType(converter);
: type.asConvertedDataType(binding);
final UDTFieldImpl<R, U> udtField = new UDTFieldImpl<R, U>(name, actualType, udt, comment, converter);
final UDTFieldImpl<R, U> udtField = new UDTFieldImpl<R, U>(name, actualType, udt, comment, binding);
return udtField;
}