[#3108] Local Fields' Converters should be preferred over globally registered Converters

This commit is contained in:
Lukas Eder 2014-03-08 13:57:48 +01:00
parent 7e59c58a1a
commit 50da819acb
26 changed files with 275 additions and 145 deletions

View File

@ -94,6 +94,7 @@ import org.jooq.tools.StringUtils;
import org.jooq.tools.reflect.Reflect; import org.jooq.tools.reflect.Reflect;
import org.jooq.tools.reflect.ReflectException; import org.jooq.tools.reflect.ReflectException;
import org.jooq.util.GeneratorStrategy.Mode; import org.jooq.util.GeneratorStrategy.Mode;
import org.jooq.util.jaxb.CustomType;
import org.jooq.util.postgres.PostgresDatabase; import org.jooq.util.postgres.PostgresDatabase;
@ -1748,13 +1749,23 @@ public class JavaGenerator extends AbstractGenerator {
final String columnId = getStrategy().getJavaIdentifier(column); final String columnId = getStrategy().getJavaIdentifier(column);
final String columnName = column.getName(); final String columnName = column.getName();
final String columnComment = StringUtils.defaultString(column.getComment()); final String columnComment = StringUtils.defaultString(column.getComment());
final CustomType columnCustomType = database.getConfiguredCustomType(column.getType().getUserType());
String isStatic = generateInstanceFields() ? "" : "static "; String isStatic = generateInstanceFields() ? "" : "static ";
String tableRef = generateInstanceFields() ? "this" : getStrategy().getJavaIdentifier(table); String tableRef = generateInstanceFields() ? "this" : getStrategy().getJavaIdentifier(table);
out.tab(1).javadoc("The column <code>%s</code>.%s", column.getQualifiedOutputName(), defaultIfBlank(" " + columnComment, "")); out.tab(1).javadoc("The column <code>%s</code>.%s", column.getQualifiedOutputName(), defaultIfBlank(" " + columnComment, ""));
out.tab(1).println("public %sfinal %s<%s, %s> %s = createField(\"%s\", %s, %s, \"%s\");",
isStatic, TableField.class, recordType, columnType, columnId, columnName, columnTypeRef, tableRef, escapeString(columnComment)); if (columnCustomType != null) {
String converter = columnCustomType.getConverter();
out.tab(1).println("public %sfinal %s<%s, %s> %s = createField(\"%s\", %s, %s, \"%s\", new %s());",
isStatic, TableField.class, recordType, columnType, columnId, columnName, columnTypeRef, tableRef, escapeString(columnComment), converter);
}
else {
out.tab(1).println("public %sfinal %s<%s, %s> %s = createField(\"%s\", %s, %s, \"%s\");",
isStatic, TableField.class, recordType, columnType, columnId, columnName, columnTypeRef, tableRef, escapeString(columnComment));
}
} }
// [#1255] With instance fields, the table constructor may // [#1255] With instance fields, the table constructor may
@ -2884,12 +2895,6 @@ public class JavaGenerator extends AbstractGenerator {
if (dataType.defaulted()) { if (dataType.defaulted()) {
sb.append(".defaulted(true)"); sb.append(".defaulted(true)");
} }
if (db.getConfiguredCustomType(u) != null) {
sb.append(".asConvertedDataType(new ");
sb.append(db.getConfiguredCustomType(u).getConverter());
sb.append("())");
}
} }
// Otherwise, reference the dialect-specific DataType itself. // Otherwise, reference the dialect-specific DataType itself.

View File

@ -51,6 +51,7 @@ import static junit.framework.Assert.fail;
// ... // ...
import static org.jooq.SQLDialect.SQLITE; import static org.jooq.SQLDialect.SQLITE;
import static org.jooq.impl.DSL.count; import static org.jooq.impl.DSL.count;
import static org.jooq.impl.DSL.table;
import java.sql.Date; import java.sql.Date;
import java.sql.Timestamp; import java.sql.Timestamp;
@ -65,6 +66,7 @@ import org.jooq.Record1;
import org.jooq.Record2; import org.jooq.Record2;
import org.jooq.Record3; import org.jooq.Record3;
import org.jooq.Record6; import org.jooq.Record6;
import org.jooq.SQLDialect;
import org.jooq.StoreQuery; import org.jooq.StoreQuery;
import org.jooq.Table; import org.jooq.Table;
import org.jooq.TableField; import org.jooq.TableField;

View File

@ -49,6 +49,7 @@ import static org.junit.Assert.assertTrue;
import java.sql.Date; import java.sql.Date;
import java.util.List; import java.util.List;
import org.jooq.DSLContext;
import org.jooq.EnumType; import org.jooq.EnumType;
import org.jooq.Field; import org.jooq.Field;
import org.jooq.Record1; import org.jooq.Record1;
@ -58,6 +59,8 @@ import org.jooq.Record6;
import org.jooq.Result; import org.jooq.Result;
import org.jooq.TableRecord; import org.jooq.TableRecord;
import org.jooq.UpdatableRecord; import org.jooq.UpdatableRecord;
import org.jooq.conf.Settings;
import org.jooq.conf.StatementType;
import org.jooq.test.BaseTest; import org.jooq.test.BaseTest;
import org.jooq.test.jOOQAbstractTest; import org.jooq.test.jOOQAbstractTest;
import org.jooq.test._.converters.Boolean_10; import org.jooq.test._.converters.Boolean_10;
@ -128,6 +131,15 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
@Test @Test
public <R extends TableRecord<R>> void testCustomEnums() throws Exception { public <R extends TableRecord<R>> void testCustomEnums() throws Exception {
testCustomEnums0(create());
}
@Test
public <R extends TableRecord<R>> void testCustomEnumsWithInline() throws Exception {
testCustomEnums0(create(new Settings().withStatementType(StatementType.STATIC_STATEMENT)));
}
private <R extends TableRecord<R>> void testCustomEnums0(DSLContext create) throws Exception {
jOOQAbstractTest.reset = false; jOOQAbstractTest.reset = false;
// This does not yet work correctly for Sybase ASE, Postgres // This does not yet work correctly for Sybase ASE, Postgres
@ -141,7 +153,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
// Insertion // Insertion
// -------------------------------------------------------------------- // --------------------------------------------------------------------
assertEquals(1, assertEquals(1,
create().insertInto(TBooleans()) create .insertInto(TBooleans())
.set(TBooleans_ID(), 1) .set(TBooleans_ID(), 1)
.set(TBooleans_BOOLEAN_10(), Boolean_10.ZERO) .set(TBooleans_BOOLEAN_10(), Boolean_10.ZERO)
.set(TBooleans_Boolean_TF_LC(), Boolean_TF_LC.FALSE) .set(TBooleans_Boolean_TF_LC(), Boolean_TF_LC.FALSE)
@ -156,7 +168,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
.execute()); .execute());
assertEquals(1, assertEquals(1,
create().insertInto(TBooleans()) create .insertInto(TBooleans())
.set(TBooleans_ID(), 2) .set(TBooleans_ID(), 2)
.set(TBooleans_BOOLEAN_10(), Boolean_10.ONE) .set(TBooleans_BOOLEAN_10(), Boolean_10.ONE)
.set(TBooleans_Boolean_TF_LC(), Boolean_TF_LC.TRUE) .set(TBooleans_Boolean_TF_LC(), Boolean_TF_LC.TRUE)
@ -171,7 +183,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
.execute()); .execute());
assertEquals(1, assertEquals(1,
create().insertInto(TBooleans()) create .insertInto(TBooleans())
.set(TBooleans_ID(), 3) .set(TBooleans_ID(), 3)
.set(TBooleans_BOOLEAN_10(), (Boolean_10) null) .set(TBooleans_BOOLEAN_10(), (Boolean_10) null)
.set(TBooleans_Boolean_TF_LC(), (Boolean_TF_LC) null) .set(TBooleans_Boolean_TF_LC(), (Boolean_TF_LC) null)
@ -188,7 +200,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
// Selection // Selection
// -------------------------------------------------------------------- // --------------------------------------------------------------------
Result<?> result = Result<?> result =
create().selectFrom(TBooleans()) create .selectFrom(TBooleans())
.where(TBooleans_ID().in(1, 2, 3)) .where(TBooleans_ID().in(1, 2, 3))
.and(TBooleans_BOOLEAN_10().in(Boolean_10.ONE, Boolean_10.ZERO) .and(TBooleans_BOOLEAN_10().in(Boolean_10.ONE, Boolean_10.ZERO)
.or(TBooleans_BOOLEAN_10().isNull())) .or(TBooleans_BOOLEAN_10().isNull()))
@ -258,7 +270,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
} }
else { else {
List<Object> b = List<Object> b =
create().selectFrom(TBooleans()) create .selectFrom(TBooleans())
.orderBy(TBooleans_ID().asc()) .orderBy(TBooleans_ID().asc())
.fetchInto(TBooleansPojo()); .fetchInto(TBooleansPojo());

View File

@ -2296,6 +2296,11 @@ public abstract class jOOQAbstractTest<
new EnumTests(this).testCustomEnums(); new EnumTests(this).testCustomEnums();
} }
@Test
public <R extends TableRecord<R>> void testCustomEnumsWithInline() throws Exception {
new EnumTests(this).testCustomEnumsWithInline();
}
@Test @Test
public void testSerialisation() throws Exception { public void testSerialisation() throws Exception {
new GeneralTests(this).testSerialisation(); new GeneralTests(this).testSerialisation();

View File

@ -56,43 +56,48 @@ xxx
xxxxxx xxxxxxxxx xxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxx xxxxxxxxxxx x xxxxxx xxxxxxxxx xxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxx xxxxxxxxxxx x
xxx xxx
x xxx xxx xxxxxxxxx xxxxx x xxx xxx xxxxxxxxx xxxxxx
xx xx
xxx xxxxxx xxx xxxxxx
xxx xxx
x xxx xxx xxxxxxxxx xxxxx xx x xxxxxx xxxxx x xxx xxx xxxxxxxxx xxxxx xx x xxxxxx xxxxxx
xx xx
xxxxxxx xxxxxxxxxx xxxxxxx xxxxxxxxxx
xxx xxx
x xxx xxx xxxxxxxxx xxxxx x xxx xxx xxxxxxxxx xxxxxx
xx xx
xxxx xxxxxxxxx xxxxxx xxxxxx xxxxxxxxxxxxx xxxx xxxxxxxxx xxxxxx xxxxxx xxxxxxxxxxxxx
xxx xxx
x xxx xxx xxxxxxxxx xxxxx x xxx xxx xxxxxxxxx xxxxxx
xx xx
xxxx xxxxxxxx xxxxxxx xxxx xxxxxxxx xxxxxxx
xxx xxx
x xxx xxx xxxxxxxxx xxxxx xx x xxxxxx xxxxx x xxx xxx xxxxxxxxx xxxxx xx x xxxxxx xxxxxx
xx xx
xxxx xxxxxxxxxxxxxx xxxxxxx xx xxxxxx xxxx xxxxxxxxxxxxxx xxxxxxx xx xxxxxx
xxx xxx
x xxx xxx xxxx xx xxx xxxxxxxxx xxxxx x xxx xxx xxxx xx xxx xxxxxxxxx xxxxxx
xx xx
xxx xxxxxxx xxx xxxxxxx
xxx xxx
x xxx xxx xxxx xx xxx xxxxx xxxx x xxx xxx xxxx xx xxx xxxxx xxxxx
xx xx
xxxxxx xxxxxxxxxx xxxxxx xxxxxxxxxx
xxx xxx
x xxx xxx xxxx xxxx xx xxx xxxxx xxxx x xxx xxx xxxx xxxx xx xxx xxxxxxx xxxx xxxxx
xx xx
xxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxxxxx
xxx
x xxx xxx xxxx xxxx xx xxx xxxxxx
xx
xxxxxxxxxxx xxxxxxxxxxxxxxx
x x
xx [/pro] */ xx [/pro] */

View File

@ -102,7 +102,9 @@ public interface BindContext extends Context<BindContext> {
* *
* @throws DataAccessException If something went wrong while binding a * @throws DataAccessException If something went wrong while binding a
* variable * variable
* @deprecated - 3.4.0 - [#3114] - Use {@link #bindValue(Object, Field)} instead
*/ */
@Deprecated
BindContext bindValue(Object value, Class<?> type) throws DataAccessException; BindContext bindValue(Object value, Class<?> type) throws DataAccessException;
/** /**
@ -110,6 +112,18 @@ public interface BindContext extends Context<BindContext> {
* *
* @throws DataAccessException If something went wrong while binding a * @throws DataAccessException If something went wrong while binding a
* variable * variable
* @deprecated - 3.4.0 - [#3114] - Use {@link #bindValue(Object, Field)} instead
*/ */
@Deprecated
BindContext bindValues(Object... values) throws DataAccessException; BindContext bindValues(Object... values) throws DataAccessException;
/**
* Bind a value using a specific type. This will also increment the internal
* counter.
*
* @throws DataAccessException If something went wrong while binding a
* variable
*/
BindContext bindValue(Object value, Field<?> field) throws DataAccessException;
} }

View File

@ -126,6 +126,15 @@ public interface Field<T> extends GroupField {
*/ */
String getComment(); String getComment();
/**
* The field's underlying {@link Converter}.
* <p>
* By default, all fields reference an identity-converter
* <code>Converter&lt;T, T></code>. Custom data types may be obtained by a
* custom {@link Converter} placed on the generated {@link TableField}.
*/
Converter<?, T> getConverter();
/** /**
* The Java type of the field. * The Java type of the field.
*/ */

View File

@ -47,8 +47,10 @@ import java.util.Collection;
import org.jooq.BindContext; import org.jooq.BindContext;
import org.jooq.Configuration; import org.jooq.Configuration;
import org.jooq.Field;
import org.jooq.QueryPart; import org.jooq.QueryPart;
import org.jooq.QueryPartInternal; import org.jooq.QueryPartInternal;
import org.jooq.exception.DataAccessException;
/** /**
* A base class for {@link BindContext} implementations * A base class for {@link BindContext} implementations
@ -89,6 +91,7 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
} }
@Override @Override
@Deprecated
public final BindContext bindValues(Object... values) { public final BindContext bindValues(Object... values) {
// [#724] When values is null, this is probably due to API-misuse // [#724] When values is null, this is probably due to API-misuse
@ -99,7 +102,7 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
else { else {
for (Object value : values) { for (Object value : values) {
Class<?> type = (value == null) ? Object.class : value.getClass(); Class<?> type = (value == null) ? Object.class : value.getClass();
bindValue(value, type); bindValue(value, DSL.val(value, type));
} }
} }
@ -107,9 +110,20 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
} }
@Override @Override
@Deprecated
public final BindContext bindValue(Object value, Class<?> type) { public final BindContext bindValue(Object value, Class<?> type) {
try { try {
return bindValue0(value, type); return bindValue0(value, DSL.val(value, type));
}
catch (SQLException e) {
throw Utils.translate(null, e);
}
}
@Override
public final BindContext bindValue(Object value, Field<?> field) throws DataAccessException {
try {
return bindValue0(value, field);
} }
catch (SQLException e) { catch (SQLException e) {
throw Utils.translate(null, e); throw Utils.translate(null, e);
@ -131,7 +145,7 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
* Subclasses may override this method to achieve different behaviour * Subclasses may override this method to achieve different behaviour
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected BindContext bindValue0(Object value, Class<?> type) throws SQLException { protected BindContext bindValue0(Object value, Field<?> field) throws SQLException {
return this; return this;
} }

View File

@ -87,6 +87,7 @@ import org.jooq.Comparator;
import org.jooq.Condition; import org.jooq.Condition;
import org.jooq.Configuration; import org.jooq.Configuration;
import org.jooq.Context; import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType; import org.jooq.DataType;
import org.jooq.DatePart; import org.jooq.DatePart;
import org.jooq.Field; import org.jooq.Field;
@ -114,17 +115,25 @@ abstract class AbstractField<T> extends AbstractQueryPart implements Field<T> {
private final String name; private final String name;
private final String comment; private final String comment;
private final DataType<T> dataType; private final DataType<T> dataType;
private final Converter<?, T> converter;
AbstractField(String name, DataType<T> type) { AbstractField(String name, DataType<T> type) {
this(name, type, null); this(name, type, null, null);
} }
AbstractField(String name, DataType<T> type, String comment) { @SuppressWarnings("unchecked")
AbstractField(String name, DataType<T> type, String comment, Converter<?, T> converter) {
super(); super();
this.name = name; this.name = name;
this.comment = defaultString(comment); this.comment = defaultString(comment);
this.dataType = type; this.dataType = type;
this.converter =
converter != null
? converter
: type instanceof ConvertedDataType
? ((ConvertedDataType<?, T>) type).converter()
: new IdentityConverter<T>(type.getType());
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -161,6 +170,11 @@ abstract class AbstractField<T> extends AbstractQueryPart implements Field<T> {
return comment; return comment;
} }
@Override
public final Converter<?, T> getConverter() {
return converter;
}
@Override @Override
public final DataType<T> getDataType() { public final DataType<T> getDataType() {
return dataType; return dataType;

View File

@ -62,6 +62,7 @@ import java.util.List;
import org.jooq.Clause; import org.jooq.Clause;
import org.jooq.Context; import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType; import org.jooq.DataType;
import org.jooq.DivideByOnStep; import org.jooq.DivideByOnStep;
import org.jooq.Field; import org.jooq.Field;
@ -325,7 +326,23 @@ abstract class AbstractTable<R extends Record> extends AbstractQueryPart impleme
* @param type The data type of the field * @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) { protected static final <R extends Record, T> TableField<R, T> createField(String name, DataType<T> type, Table<R> table, String comment) {
final TableFieldImpl<R, T> tableField = new TableFieldImpl<R, T>(name, type, table, comment); return createField(name, type, table, comment, 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
*/
@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
? (DataType<U>) type
: type.asConvertedDataType(converter);
final TableFieldImpl<R, U> tableField = new TableFieldImpl<R, U>(name, actualType, table, comment, converter);
// [#1199] The public API of Table returns immutable field lists // [#1199] The public API of Table returns immutable field lists
if (table instanceof TableImpl) { if (table instanceof TableImpl) {

View File

@ -57,12 +57,18 @@ xxxxx xxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx x
xxxxxxx xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxx xxxxxxx xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxx
xxxxxxx xxxxx x xxxxxx xxxxxxx xxxxx x xxxxxx
xxxxxxx xxxxx xxxxxxxxxxx xxxxxxxxx
xxxxxxxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxx xx xxxxxxxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxx xx
xxxxxxxxxxxxxxx xxxxxx x xxxxxxxxxxxxxxx xxxxxx x
xxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxx x xxxxxx xxxxxxxxxx x xxxxxx
xxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxx
x
xxxxx xxxxxxxxxxx xxxxxxxxxxxxx x
xxxxxx xxxxxxxxx
x x
xxxxxxxxx xxxxxxxxx
@ -88,7 +94,7 @@ xxxxx xxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx x
xxxxxxxxx xxxxxxxxx
xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxx xxxxxxxx x xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxx xxxxxxxx x
xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxx xxxxxx
x x
x x
xx [/pro] */ xx [/pro] */

View File

@ -72,6 +72,7 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx
xxxxxxx xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxx xxxxxxx xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxx
xxxxxxx xxxxx xxxxxx xxxxxxx xxxxxxx xxxxx xxxxxx xxxxxxx
xxxxxxx xxxxx xxxxxxxxxxx xxxxxxxxx
xxxxxxx xxxxx xxxxxxxxxxx xxxxx xxxxxxx xxxxx xxxxxxxxxxx xxxxx
xxxxxxx xxxxx xxxxxx xxxxx xxxxxxx xxxxx xxxxxx xxxxx
xxxxxxx xxx xxxxxx xxxxxxx xxx xxxxxx
@ -85,10 +86,10 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx
xxxxxxxxxxx x xxxxxxx xxxxxxxxxxx x xxxxxxx
xxxxxxxxx x xxxxx xxxxxxxxx x xxxxx
xxxxxxxxx x xxxxx xxxxxxxxxxxxx x xxxxx
xx xxxxx xxxx xxxx xxxxxxxxxxxxxx xx xxxxx xxxx xxxx xxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x x
xxx xxx
@ -106,7 +107,7 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx
xxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx x xxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx x
xxxxxxxxxxxxxxxx xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxx
xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x
xxx xx xxxxxxx x xxxxxx x xxx xx xxxxxxx x xxxxxx x
xxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx
x x
@ -128,7 +129,7 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxxx
xxxxxx xxxxx xxx xxxxx x xxxxxx xxxxx xxx xxxxx x
xx xxxxxx xx xxxxx x xx xxxxxx xx xxxxx x
xxxxxx xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxx xxxxxx xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxx
x x
xxxx x xxxx x
xxxxxx xxxxxx xxxxxx xxxxxx
@ -162,7 +163,7 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx
xx xxxxxx xxxxx xxxx xxxx xx xxxx xx xxxxxxx xxxx xx xxxxxx xx xx xxxxxx xxxxx xxxx xxxx xx xxxx xx xxxxxxx xxxx xx xxxxxx xx
xx xxxxxxxxx xxxxxxx xxx xxxxxx xxxxxxx xx xxxxxxxxx xxxxxxx xxx xxxxxx xxxxxxx
x x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxx x xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx xxxxxxxxxx x xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx
x x
x x
@ -173,7 +174,7 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx
xxxxx x xxxxx xxxxx x xxxxx
x x
xxxx x xxxx x
xxxxx x xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxx xxxxx x xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxx
x x
x x
@ -198,6 +199,11 @@ xxxxxx xxxxx xxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxxx
xxxxxx xxxxx xxxxxxxxxxx xxxxxxxxxxxxx x xxxxxx xxxxx xxxxxxxxxxx xxxxxxxxxxxxx x
xxxxxx xxxxxxxxx
x
xxxxxxxxx
xxxxxx xxxxx xxxxxxxxxxx xxxxxxxxxxxxxx x
xxxxxx xxxxx xxxxxx xxxxx
x x

View File

@ -93,7 +93,7 @@ class ArrayTable extends AbstractTable<Record> {
/* [pro] xx /* [pro] xx
xx xxxxxxx xxxx xxxxx xx xxxxxxx xxxx xxxxxxxxxxx xx xxxxxx xxxxxx x xxxxx xxxxx xx xxxxxxx xxxx xxxxx xx xxxxxxx xxxx xxxxxxxxxxx xx xxxxxx xxxxxx x xxxxx xxxxx
xxxx xx xxxxxx xxxxxxxxxx xxxxxxxxxxxxxx x xxxx xx xxxxxx xxxxxxxxxx xxxxxxxxxxxxxx x
xxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxx x xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x x
xx xxxxxxx xxxx xxxxx xx xxxxxxx xxxx xxxxxxxxxxx xx xxxxxx xx xxxxxxx xxxx xxxxx xx xxxxxxx xxxx xxxxxxxxxxx xx xxxxxx

View File

@ -64,8 +64,6 @@ class ConvertedDataType<T, U> extends DefaultDataType<U> {
this.delegate = delegate; this.delegate = delegate;
this.converter = converter; this.converter = converter;
DataTypes.registerConverter(converter.toType(), converter);
} }
@Override @Override
@ -88,4 +86,8 @@ class ConvertedDataType<T, U> extends DefaultDataType<U> {
public U convert(Object object) { public U convert(Object object) {
return converter.from(delegate.convert(converter.to((U) object))); return converter.from(delegate.convert(converter.to((U) object)));
} }
Converter<? super T, U> converter() {
return converter;
}
} }

View File

@ -48,9 +48,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.jooq.Converter;
import org.jooq.DataType; import org.jooq.DataType;
import org.jooq.exception.DataTypeException;
/** /**
* A central {@link DataType} registry * A central {@link DataType} registry
@ -59,7 +57,6 @@ import org.jooq.exception.DataTypeException;
*/ */
final class DataTypes { final class DataTypes {
private static final Map<Class<?>, Converter<?, ?>> CONVERTERS = new HashMap<Class<?>, Converter<?, ?>>();
private static final Map<String, Class<?>> UDT_RECORDS = new HashMap<String, Class<?>>(); private static final Map<String, Class<?>> UDT_RECORDS = new HashMap<String, Class<?>>();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -67,52 +64,6 @@ final class DataTypes {
// (this may be rendered public in the future) // (this may be rendered public in the future)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/**
* Register a <code>Converter</code> for a custom type
* <p>
* This registers a {@link Converter} for a custom type. This converter will
* be used by jOOQ to recognise custom types and to transform them back to
* well-known database types (as defined in {@link Converter#fromType()}) in
* rendering and binding steps
* <p>
* A custom type can be registered only once. Duplicate registrations will
* be ignored
* <p>
* The converter class must provide a default constructor.
*
* @see #registerConverter(Class, Converter)
*/
static final synchronized <U> void registerConverter(Class<U> customType,
Class<? extends Converter<?, U>> converter) {
try {
converter.getConstructor().setAccessible(true);
registerConverter(customType, converter.newInstance());
}
catch (Exception e) {
throw new DataTypeException("Cannot register converter", e);
}
}
/**
* Register a <code>Converter</code> for a custom type
* <p>
* This registers a {@link Converter} for a custom type. This converter will
* be used by jOOQ to recognise custom types and to transform them back to
* well-known database types (as defined in {@link Converter#fromType()}) in
* rendering and binding steps
* <p>
* A custom type can be registered only once. Duplicate registrations will
* be ignored
*/
static final synchronized <U> void registerConverter(Class<U> customType, Converter<?, U> converter) {
// A converter can be registered only once
if (!CONVERTERS.containsKey(customType)) {
CONVERTERS.put(customType, converter);
}
}
/** /**
* Register a type mapping for a UDT * Register a type mapping for a UDT
* <p> * <p>
@ -134,13 +85,6 @@ final class DataTypes {
// XXX: Internal API // XXX: Internal API
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@SuppressWarnings("unchecked")
static final <U> Converter<?, U> converter(Class<U> customType) {
// TODO: Is synchronisation needed? How to implement it most efficiently?
return (Converter<?, U>) CONVERTERS.get(customType);
}
static final Map<String, Class<?>> udtRecords() { static final Map<String, Class<?>> udtRecords() {
return Collections.unmodifiableMap(UDT_RECORDS); return Collections.unmodifiableMap(UDT_RECORDS);
} }

View File

@ -72,6 +72,7 @@ import org.jooq.BindContext;
import org.jooq.Configuration; import org.jooq.Configuration;
import org.jooq.Converter; import org.jooq.Converter;
import org.jooq.EnumType; import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.SQLDialect; import org.jooq.SQLDialect;
import org.jooq.UDTRecord; import org.jooq.UDTRecord;
import org.jooq.exception.SQLDialectNotSupportedException; import org.jooq.exception.SQLDialectNotSupportedException;
@ -106,15 +107,13 @@ class DefaultBindContext extends AbstractBindContext {
@Override @Override
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
protected final BindContext bindValue0(Object value, Class<?> type) throws SQLException { protected final BindContext bindValue0(Object value, Field<?> field) throws SQLException {
SQLDialect dialect = configuration.dialect(); SQLDialect dialect = configuration.dialect();
// [#650] Check first, if we have a converter for the supplied type // [#650] [#3108] Use the Field's Converter before actually binding any value
Converter<?, ?> converter = DataTypes.converter(type); Converter<?, ?> converter = field.getConverter();
if (converter != null) { Class<?> type = converter.fromType();
value = ((Converter) converter).to(value); value = ((Converter) converter).to(value);
type = converter.fromType();
}
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
if (value != null && value.getClass().isArray() && value.getClass() != byte[].class) { if (value != null && value.getClass().isArray() && value.getClass() != byte[].class) {

View File

@ -126,4 +126,9 @@ public class EnumConverter<T, U extends Enum<U>> implements Converter<T, U> {
*/ */
STRING STRING
} }
@Override
public String toString() {
return "EnumConverter [ from : " + fromType.getName() + ", to : " + toType.getName() + " ]";
}
} }

View File

@ -0,0 +1,84 @@
/**
* Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com)
* All rights reserved.
*
* This work is dual-licensed
* - under the Apache Software License 2.0 (the "ASL")
* - under the jOOQ License and Maintenance Agreement (the "jOOQ License")
* =============================================================================
* You may choose which license applies to you:
*
* - If you're using this work with Open Source databases, you may choose
* either ASL or jOOQ License.
* - If you're using this work with at least one commercial database, you must
* choose jOOQ License
*
* For more information, please visit http://www.jooq.org/licenses
*
* Apache Software License 2.0:
* -----------------------------------------------------------------------------
* 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.
*
* jOOQ License and Maintenance Agreement:
* -----------------------------------------------------------------------------
* Data Geekery grants the Customer the non-exclusive, timely limited and
* non-transferable license to install and use the Software under the terms of
* the jOOQ License and Maintenance Agreement.
*
* This library is distributed with a LIMITED WARRANTY. See the jOOQ License
* and Maintenance Agreement for more details: http://www.jooq.org/licensing
*/
package org.jooq.impl;
import org.jooq.Converter;
/**
* @author Lukas Eder
*/
class IdentityConverter<T> implements Converter<T, T> {
/**
* Generated UID
*/
private static final long serialVersionUID = -1721687282753727624L;
private final Class<T> type;
IdentityConverter(Class<T> type) {
this.type = type;
}
@Override
public final T from(T t) {
return t;
}
@Override
public final T to(T t) {
return t;
}
@Override
public final Class<T> fromType() {
return type;
}
@Override
public final Class<T> toType() {
return type;
}
@Override
public String toString() {
return "IdentityConverter [" + type.getName() + "]";
}
}

View File

@ -47,6 +47,7 @@ import java.util.Map;
import org.jooq.BindContext; import org.jooq.BindContext;
import org.jooq.Configuration; import org.jooq.Configuration;
import org.jooq.Field;
import org.jooq.Param; import org.jooq.Param;
import org.jooq.QueryPart; import org.jooq.QueryPart;
import org.jooq.QueryPartInternal; import org.jooq.QueryPartInternal;
@ -90,7 +91,7 @@ class ParamCollector extends AbstractBindContext {
} }
@Override @Override
protected final BindContext bindValue0(Object value, Class<?> type) throws SQLException { protected final BindContext bindValue0(Object value, Field<?> field) throws SQLException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }

View File

@ -100,7 +100,7 @@ class TableAlias<R extends Record> extends AbstractTable<R> {
name = fieldAliases[i]; name = fieldAliases[i];
} }
result.add(new TableFieldImpl(name, field.getDataType(), this, field.getComment())); result.add(new TableFieldImpl(name, field.getDataType(), this, field.getComment(), field.getConverter()));
} }
return new Fields<R>(result); return new Fields<R>(result);

View File

@ -48,6 +48,7 @@ import static org.jooq.impl.Utils.DATA_OMIT_CLAUSE_EVENT_EMISSION;
import org.jooq.BindContext; import org.jooq.BindContext;
import org.jooq.Clause; import org.jooq.Clause;
import org.jooq.Context; import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType; import org.jooq.DataType;
import org.jooq.Record; import org.jooq.Record;
import org.jooq.RenderContext; import org.jooq.RenderContext;
@ -67,8 +68,8 @@ class TableFieldImpl<R extends Record, T> extends AbstractField<T> implements Ta
private final Table<R> table; private final Table<R> table;
TableFieldImpl(String name, DataType<T> type, Table<R> table, String comment) { TableFieldImpl(String name, DataType<T> type, Table<R> table, String comment, Converter<?, T> converter) {
super(name, type, comment); super(name, type, comment, converter);
this.table = table; this.table = table;
} }

View File

@ -162,7 +162,7 @@ class UDTConstant<R extends UDTRecord<R>> extends AbstractParam<R> {
xx xxxxxx xxxxxxxx xxxxxxxxxxxxxxxxx xxxxx xxx xxxxxx xxx xx xxxxx xx xxxxxx xxxxxxxx xxxxxxxxxxxxxxxxx xxxxx xxx xxxxxx xxx xx xxxxx
xx xx xxx xxxxxxxxxxxxxxxxx xxxxxxxx xx xx xxx xxxxxxxxxxxxxxxxx xxxxxxxx
xxxx xxxxxxx xxxx xxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxx xxxxxx
xxxxxx xxxxxx
xx xx xxx xxx xxxx xxxxxxxx xxxxxx xx xx xxxxxxx xxxx xxx xxxxxxxx xxxxx xx xx xxx xxx xxxx xxxxxxxx xxxxxx xx xx xxxxxxx xxxx xxx xxxxxxxx xxxxx

View File

@ -2255,14 +2255,8 @@ final class Utils {
static final <T, U> U getFromResultSet(ExecuteContext ctx, Field<U> field, int index) throws SQLException { static final <T, U> U getFromResultSet(ExecuteContext ctx, Field<U> field, int index) throws SQLException {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Converter<T, U> converter = (Converter<T, U>) DataTypes.converter(field.getType()); Converter<T, U> converter = (Converter<T, U>) field.getConverter();
return converter.from(getFromResultSet(ctx, converter.fromType(), index));
if (converter != null) {
return converter.from(getFromResultSet(ctx, converter.fromType(), index));
}
else {
return getFromResultSet(ctx, field.getType(), index);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -108,7 +108,7 @@ class Val<T> extends AbstractParam<T> {
// Casting can be enforced or prevented // Casting can be enforced or prevented
switch (context.castMode()) { switch (context.castMode()) {
case NEVER: case NEVER:
toSQL(context, value, getType()); toSQL(context, value, getConverter());
return; return;
case ALWAYS: case ALWAYS:
@ -127,7 +127,7 @@ class Val<T> extends AbstractParam<T> {
toSQLCast(context); toSQLCast(context);
} }
else { else {
toSQL(context, value, getType()); toSQL(context, value, getConverter());
} }
return; return;
@ -140,7 +140,7 @@ class Val<T> extends AbstractParam<T> {
// Most RDBMS can infer types for bind values // Most RDBMS can infer types for bind values
else { else {
toSQL(context, value, getType()); toSQL(context, value, getConverter());
} }
} }
@ -245,7 +245,7 @@ class Val<T> extends AbstractParam<T> {
// [#1125] Also with temporal data types, casting is needed some times // [#1125] Also with temporal data types, casting is needed some times
// [#1130] TODO type can be null for ARRAY types, etc. // [#1130] TODO type can be null for ARRAY types, etc.
else if (family == POSTGRES && (type == null || !type.isTemporal())) { else if (family == POSTGRES && (type == null || !type.isTemporal())) {
toSQL(context, value, getType()); toSQL(context, value, getConverter());
} }
// [#1727] VARCHAR types should be cast to their actual lengths in some // [#1727] VARCHAR types should be cast to their actual lengths in some
@ -291,7 +291,7 @@ class Val<T> extends AbstractParam<T> {
private final void toSQLCast(RenderContext context, DataType<?> type, int length, int precision, int scale) { private final void toSQLCast(RenderContext context, DataType<?> type, int length, int precision, int scale) {
context.keyword("cast").sql("("); context.keyword("cast").sql("(");
toSQL(context, value, getType()); toSQL(context, value, getConverter());
context.sql(" ").keyword("as").sql(" ") context.sql(" ").keyword("as").sql(" ")
.sql(type.length(length).precision(precision, scale).getCastTypeName(context.configuration())) .sql(type.length(length).precision(precision, scale).getCastTypeName(context.configuration()))
.sql(")"); .sql(")");
@ -317,31 +317,16 @@ class Val<T> extends AbstractParam<T> {
} }
} }
/**
* Inlining abstraction
*/
private final void toSQL(RenderContext context, Object val) {
if (val == null) {
toSQL(context, val, Object.class);
}
else {
toSQL(context, val, val.getClass());
}
}
/** /**
* Inlining abstraction * Inlining abstraction
*/ */
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
private final void toSQL(RenderContext context, Object val, Class<?> type) { private final void toSQL(RenderContext context, Object val, Converter<?, T> converter) {
SQLDialect family = context.configuration().dialect().family(); SQLDialect family = context.configuration().dialect().family();
// [#650] Check first, if we have a converter for the supplied type // [#650] [#3108] Check first, if we have a converter for the supplied type
Converter<?, ?> converter = DataTypes.converter(type); Class<?> type = converter.fromType();
if (converter != null) { val = ((Converter) converter).to(val);
val = ((Converter) converter).to(val);
type = converter.fromType();
}
if (isInline(context)) { if (isInline(context)) {
// [#2223] Some type-casts in this section may seem unnecessary, e.g. // [#2223] Some type-casts in this section may seem unnecessary, e.g.
@ -511,7 +496,7 @@ class Val<T> extends AbstractParam<T> {
for (Object o : ((Object[]) val)) { for (Object o : ((Object[]) val)) {
context.sql(separator); context.sql(separator);
toSQL(context, o, type.getComponentType()); toSQL(context, o, new IdentityConverter(type.getComponentType()));
separator = ", "; separator = ", ";
} }
@ -525,7 +510,7 @@ class Val<T> extends AbstractParam<T> {
for (Object o : ((Object[]) val)) { for (Object o : ((Object[]) val)) {
context.sql(separator); context.sql(separator);
toSQL(context, o, type.getComponentType()); toSQL(context, o, new IdentityConverter(type.getComponentType()));
separator = ", "; separator = ", ";
} }
@ -538,7 +523,14 @@ class Val<T> extends AbstractParam<T> {
x x
xx [/pro] */ xx [/pro] */
else if (EnumType.class.isAssignableFrom(type)) { else if (EnumType.class.isAssignableFrom(type)) {
toSQL(context, ((EnumType) val).getLiteral()); String literal = ((EnumType) val).getLiteral();
if (literal == null) {
toSQL(context, val, new IdentityConverter(String.class));
}
else {
toSQL(context, val, new IdentityConverter(String.class));
}
} }
else if (UDTRecord.class.isAssignableFrom(type)) { else if (UDTRecord.class.isAssignableFrom(type)) {
context.sql("[UDT]"); context.sql("[UDT]");
@ -609,7 +601,7 @@ class Val<T> extends AbstractParam<T> {
// [#1302] Bind value only if it was not explicitly forced to be inlined // [#1302] Bind value only if it was not explicitly forced to be inlined
if (!isInline()) { if (!isInline()) {
context.bindValue(value, getType()); context.bindValue(value, this);
} }
} }

View File

@ -48,7 +48,6 @@ import org.jooq.Record;
import org.jooq.RenderContext; import org.jooq.RenderContext;
import org.jooq.Row; import org.jooq.Row;
import org.jooq.Select; import org.jooq.Select;
import org.jooq.Support;
import org.jooq.Table; import org.jooq.Table;
/** /**

View File

@ -973,7 +973,7 @@ public class BasicTest extends AbstractTest {
public void bind(BindContext ctx) { public void bind(BindContext ctx) {
try { try {
ctx.statement().setInt(ctx.nextIndex(), 1); ctx.statement().setInt(ctx.nextIndex(), 1);
ctx.bindValues(1); ctx.bindValue(1, DSL.val(1));
} }
catch (SQLException ignore) {} catch (SQLException ignore) {}
} }
@ -1064,7 +1064,7 @@ public class BasicTest extends AbstractTest {
@Override @Override
public void bind(BindContext ctx) { public void bind(BindContext ctx) {
ctx.bindValues(1); ctx.bindValue(1, DSL.val(1));
} }
}; };