[#3108] Local Fields' Converters should be preferred over globally registered Converters
This commit is contained in:
parent
7e59c58a1a
commit
50da819acb
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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());
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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] */
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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<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.
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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] */
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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() + " ]";
|
||||
}
|
||||
}
|
||||
|
||||
84
jOOQ/src/main/java/org/jooq/impl/IdentityConverter.java
Normal file
84
jOOQ/src/main/java/org/jooq/impl/IdentityConverter.java
Normal 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() + "]";
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user