[#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.ReflectException;
import org.jooq.util.GeneratorStrategy.Mode;
import org.jooq.util.jaxb.CustomType;
import org.jooq.util.postgres.PostgresDatabase;
@ -1748,13 +1749,23 @@ public class JavaGenerator extends AbstractGenerator {
final String columnId = getStrategy().getJavaIdentifier(column);
final String columnName = column.getName();
final String columnComment = StringUtils.defaultString(column.getComment());
final CustomType columnCustomType = database.getConfiguredCustomType(column.getType().getUserType());
String isStatic = generateInstanceFields() ? "" : "static ";
String tableRef = generateInstanceFields() ? "this" : getStrategy().getJavaIdentifier(table);
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
@ -2884,12 +2895,6 @@ public class JavaGenerator extends AbstractGenerator {
if (dataType.defaulted()) {
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.

View File

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

View File

@ -49,6 +49,7 @@ import static org.junit.Assert.assertTrue;
import java.sql.Date;
import java.util.List;
import org.jooq.DSLContext;
import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.Record1;
@ -58,6 +59,8 @@ import org.jooq.Record6;
import org.jooq.Result;
import org.jooq.TableRecord;
import org.jooq.UpdatableRecord;
import org.jooq.conf.Settings;
import org.jooq.conf.StatementType;
import org.jooq.test.BaseTest;
import org.jooq.test.jOOQAbstractTest;
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
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;
// 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
// --------------------------------------------------------------------
assertEquals(1,
create().insertInto(TBooleans())
create .insertInto(TBooleans())
.set(TBooleans_ID(), 1)
.set(TBooleans_BOOLEAN_10(), Boolean_10.ZERO)
.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());
assertEquals(1,
create().insertInto(TBooleans())
create .insertInto(TBooleans())
.set(TBooleans_ID(), 2)
.set(TBooleans_BOOLEAN_10(), Boolean_10.ONE)
.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());
assertEquals(1,
create().insertInto(TBooleans())
create .insertInto(TBooleans())
.set(TBooleans_ID(), 3)
.set(TBooleans_BOOLEAN_10(), (Boolean_10) 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
// --------------------------------------------------------------------
Result<?> result =
create().selectFrom(TBooleans())
create .selectFrom(TBooleans())
.where(TBooleans_ID().in(1, 2, 3))
.and(TBooleans_BOOLEAN_10().in(Boolean_10.ONE, Boolean_10.ZERO)
.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 {
List<Object> b =
create().selectFrom(TBooleans())
create .selectFrom(TBooleans())
.orderBy(TBooleans_ID().asc())
.fetchInto(TBooleansPojo());

View File

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

View File

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

View File

@ -102,7 +102,9 @@ public interface BindContext extends Context<BindContext> {
*
* @throws DataAccessException If something went wrong while binding a
* variable
* @deprecated - 3.4.0 - [#3114] - Use {@link #bindValue(Object, Field)} instead
*/
@Deprecated
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
* variable
* @deprecated - 3.4.0 - [#3114] - Use {@link #bindValue(Object, Field)} instead
*/
@Deprecated
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();
/**
* 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.
*/

View File

@ -47,8 +47,10 @@ import java.util.Collection;
import org.jooq.BindContext;
import org.jooq.Configuration;
import org.jooq.Field;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
import org.jooq.exception.DataAccessException;
/**
* A base class for {@link BindContext} implementations
@ -89,6 +91,7 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
}
@Override
@Deprecated
public final BindContext bindValues(Object... values) {
// [#724] When values is null, this is probably due to API-misuse
@ -99,7 +102,7 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
else {
for (Object value : values) {
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
@Deprecated
public final BindContext bindValue(Object value, Class<?> type) {
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) {
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
*/
@SuppressWarnings("unused")
protected BindContext bindValue0(Object value, Class<?> type) throws SQLException {
protected BindContext bindValue0(Object value, Field<?> field) throws SQLException {
return this;
}

View File

@ -87,6 +87,7 @@ import org.jooq.Comparator;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType;
import org.jooq.DatePart;
import org.jooq.Field;
@ -114,17 +115,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;
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();
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());
}
// ------------------------------------------------------------------------
@ -161,6 +170,11 @@ abstract class AbstractField<T> extends AbstractQueryPart implements Field<T> {
return comment;
}
@Override
public final Converter<?, T> getConverter() {
return converter;
}
@Override
public final DataType<T> getDataType() {
return dataType;

View File

@ -62,6 +62,7 @@ import java.util.List;
import org.jooq.Clause;
import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType;
import org.jooq.DivideByOnStep;
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
*/
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
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 xxxxx x xxxxxx
xxxxxxx xxxxx xxxxxxxxxxx xxxxxxxxx
xxxxxxxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxx xx
xxxxxxxxxxxxxxx xxxxxx x
xxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxx x xxxxxx
xxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxx
x
xxxxx xxxxxxxxxxx xxxxxxxxxxxxx x
xxxxxx xxxxxxxxx
x
xxxxxxxxx
@ -88,7 +94,7 @@ xxxxx xxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx x
xxxxxxxxx
xxxxxx xxxxx xxxx xxxxxxxxxxxxxxxx xxxxxxxx x
xxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx xxxxxx
x
x
xx [/pro] */

View File

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

View File

@ -93,7 +93,7 @@ class ArrayTable extends AbstractTable<Record> {
/* [pro] xx
xx xxxxxxx xxxx xxxxx xx xxxxxxx xxxx xxxxxxxxxxx xx xxxxxx xxxxxx x xxxxx xxxxx
xxxx xx xxxxxx xxxxxxxxxx xxxxxxxxxxxxxx x
xxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxx x xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x
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.converter = converter;
DataTypes.registerConverter(converter.toType(), converter);
}
@Override
@ -88,4 +86,8 @@ class ConvertedDataType<T, U> extends DefaultDataType<U> {
public U convert(Object 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.Map;
import org.jooq.Converter;
import org.jooq.DataType;
import org.jooq.exception.DataTypeException;
/**
* A central {@link DataType} registry
@ -59,7 +57,6 @@ import org.jooq.exception.DataTypeException;
*/
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<?>>();
// ------------------------------------------------------------------------
@ -67,52 +64,6 @@ final class DataTypes {
// (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
* <p>
@ -134,13 +85,6 @@ final class DataTypes {
// 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() {
return Collections.unmodifiableMap(UDT_RECORDS);
}

View File

@ -72,6 +72,7 @@ import org.jooq.BindContext;
import org.jooq.Configuration;
import org.jooq.Converter;
import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.SQLDialect;
import org.jooq.UDTRecord;
import org.jooq.exception.SQLDialectNotSupportedException;
@ -106,15 +107,13 @@ class DefaultBindContext extends AbstractBindContext {
@Override
@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();
// [#650] Check first, if we have a converter for the supplied type
Converter<?, ?> converter = DataTypes.converter(type);
if (converter != null) {
value = ((Converter) converter).to(value);
type = converter.fromType();
}
// [#650] [#3108] Use the Field's Converter before actually binding any value
Converter<?, ?> converter = field.getConverter();
Class<?> type = converter.fromType();
value = ((Converter) converter).to(value);
if (log.isTraceEnabled()) {
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
}
@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.Configuration;
import org.jooq.Field;
import org.jooq.Param;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
@ -90,7 +91,7 @@ class ParamCollector extends AbstractBindContext {
}
@Override
protected final BindContext bindValue0(Object value, Class<?> type) throws SQLException {
protected final BindContext bindValue0(Object value, Field<?> field) throws SQLException {
throw new UnsupportedOperationException();
}
}

View File

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

View File

@ -48,6 +48,7 @@ import static org.jooq.impl.Utils.DATA_OMIT_CLAUSE_EVENT_EMISSION;
import org.jooq.BindContext;
import org.jooq.Clause;
import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType;
import org.jooq.Record;
import org.jooq.RenderContext;
@ -67,8 +68,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) {
super(name, type, comment);
TableFieldImpl(String name, DataType<T> type, Table<R> table, String comment, Converter<?, T> converter) {
super(name, type, comment, converter);
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 xx xxx xxxxxxxxxxxxxxxxx xxxxxxxx
xxxx xxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx xxxxxx
xxxxxx
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 {
@SuppressWarnings("unchecked")
Converter<T, U> converter = (Converter<T, U>) DataTypes.converter(field.getType());
if (converter != null) {
return converter.from(getFromResultSet(ctx, converter.fromType(), index));
}
else {
return getFromResultSet(ctx, field.getType(), index);
}
Converter<T, U> converter = (Converter<T, U>) field.getConverter();
return converter.from(getFromResultSet(ctx, converter.fromType(), index));
}
@SuppressWarnings("unchecked")

View File

@ -108,7 +108,7 @@ class Val<T> extends AbstractParam<T> {
// Casting can be enforced or prevented
switch (context.castMode()) {
case NEVER:
toSQL(context, value, getType());
toSQL(context, value, getConverter());
return;
case ALWAYS:
@ -127,7 +127,7 @@ class Val<T> extends AbstractParam<T> {
toSQLCast(context);
}
else {
toSQL(context, value, getType());
toSQL(context, value, getConverter());
}
return;
@ -140,7 +140,7 @@ class Val<T> extends AbstractParam<T> {
// Most RDBMS can infer types for bind values
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
// [#1130] TODO type can be null for ARRAY types, etc.
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
@ -291,7 +291,7 @@ class Val<T> extends AbstractParam<T> {
private final void toSQLCast(RenderContext context, DataType<?> type, int length, int precision, int scale) {
context.keyword("cast").sql("(");
toSQL(context, value, getType());
toSQL(context, value, getConverter());
context.sql(" ").keyword("as").sql(" ")
.sql(type.length(length).precision(precision, scale).getCastTypeName(context.configuration()))
.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
*/
@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();
// [#650] Check first, if we have a converter for the supplied type
Converter<?, ?> converter = DataTypes.converter(type);
if (converter != null) {
val = ((Converter) converter).to(val);
type = converter.fromType();
}
// [#650] [#3108] Check first, if we have a converter for the supplied type
Class<?> type = converter.fromType();
val = ((Converter) converter).to(val);
if (isInline(context)) {
// [#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)) {
context.sql(separator);
toSQL(context, o, type.getComponentType());
toSQL(context, o, new IdentityConverter(type.getComponentType()));
separator = ", ";
}
@ -525,7 +510,7 @@ class Val<T> extends AbstractParam<T> {
for (Object o : ((Object[]) val)) {
context.sql(separator);
toSQL(context, o, type.getComponentType());
toSQL(context, o, new IdentityConverter(type.getComponentType()));
separator = ", ";
}
@ -538,7 +523,14 @@ class Val<T> extends AbstractParam<T> {
x
xx [/pro] */
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)) {
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
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.Row;
import org.jooq.Select;
import org.jooq.Support;
import org.jooq.Table;
/**

View File

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