diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java index 7b72e8fdd5..6307ace869 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java @@ -2589,10 +2589,10 @@ public class JavaGenerator extends AbstractGenerator { out.tab(1).javadoc("The parameter %s.%s", parameter.getQualifiedOutputName(), defaultIfBlank(" " + paramComment, "")); if (paramConverterType != null) - out.tab(2).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s, new %s());", + out.tab(1).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s, new %s());", Parameter.class, paramType, paramId, paramName, paramTypeRef, isDefaulted, paramConverterType); else - out.tab(2).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s);", + out.tab(1).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s);", Parameter.class, paramType, paramId, paramName, paramTypeRef, isDefaulted); } diff --git a/jOOQ/src/main/java/org/jooq/Parameter.java b/jOOQ/src/main/java/org/jooq/Parameter.java index 380cd50638..0810292b4b 100644 --- a/jOOQ/src/main/java/org/jooq/Parameter.java +++ b/jOOQ/src/main/java/org/jooq/Parameter.java @@ -60,6 +60,15 @@ public interface Parameter extends QueryPart { */ Class getType(); + /** + * The parameter's underlying {@link Converter}. + *

+ * By default, all parameters reference an identity-converter + * Converter<T, T>. Custom data types may be obtained by a + * custom {@link Converter} placed on the generated {@link Parameter}. + */ + Converter getConverter(); + /** * The type of this parameter (might not be dialect-specific) */ diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index e41262f8ad..bed7a39b89 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -560,7 +560,7 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro getOutParameters().contains(parameter)) { int index = parameterIndexes.get(parameter); - results.put(parameter, Utils.getFromStatement(ctx, parameter.getType(), index)); + results.put(parameter, Utils.getFromStatement(ctx, parameter, index)); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java index b088fb78c9..de55220f24 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java @@ -56,11 +56,12 @@ import org.jooq.tools.StringUtils; */ class ParameterImpl extends AbstractQueryPart implements Parameter { - private static final long serialVersionUID = -5277225593751085577L; + private static final long serialVersionUID = -5277225593751085577L; - private final String name; - private final DataType type; - private final boolean isDefaulted; + private final String name; + private final DataType type; + private final Converter converter; + private final boolean isDefaulted; @SuppressWarnings({ "unchecked", "rawtypes" }) ParameterImpl(String name, DataType type, boolean isDefaulted, Converter converter) { @@ -69,6 +70,13 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { this.type = converter == null ? (DataType) type : type.asConvertedDataType((Converter) converter); + + this.converter = + converter != null + ? converter + : type instanceof ConvertedDataType + ? ((ConvertedDataType) type).converter() + : new IdentityConverter((Class) type.getType()); } @Override @@ -76,6 +84,11 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { return name; } + @Override + public final Converter getConverter() { + return converter; + } + @Override public final DataType getDataType() { return type; diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index f1797f239a..30d35711e6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -123,6 +123,7 @@ import org.jooq.ExecuteListener; import org.jooq.Field; import org.jooq.Name; import org.jooq.Param; +import org.jooq.Parameter; import org.jooq.QueryPart; import org.jooq.Record; import org.jooq.RecordType; @@ -2278,25 +2279,30 @@ final class Utils { } @SuppressWarnings("unchecked") - static final T getFromSQLInput(Configuration configuration, SQLInput stream, Field field) throws SQLException { - Class type = field.getType(); - DataType dataType = field.getDataType(); + static final U getFromSQLInput(Configuration configuration, SQLInput stream, Field field) throws SQLException { + DataType dataType = field.getDataType(); + + // [#650] [#2155] [#3108] Use the Field's Converter before actually binding any value + Converter converter = (Converter) field.getConverter(); + Class type = converter.fromType(); + + T result = null; if (type == Blob.class) { - return (T) stream.readBlob(); + result = (T) stream.readBlob(); } else if (type == Boolean.class) { - return (T) wasNull(stream, Boolean.valueOf(stream.readBoolean())); + result = (T) wasNull(stream, Boolean.valueOf(stream.readBoolean())); } else if (type == BigInteger.class) { - BigDecimal result = stream.readBigDecimal(); - return (T) (result == null ? null : result.toBigInteger()); + BigDecimal d = stream.readBigDecimal(); + result = (T) (d == null ? null : d.toBigInteger()); } else if (type == BigDecimal.class) { - return (T) stream.readBigDecimal(); + result = (T) stream.readBigDecimal(); } else if (type == Byte.class) { - return (T) wasNull(stream, Byte.valueOf(stream.readByte())); + result = (T) wasNull(stream, Byte.valueOf(stream.readByte())); } else if (type == byte[].class) { @@ -2305,98 +2311,105 @@ final class Utils { Blob blob = null; try { blob = stream.readBlob(); - return (T) (blob == null ? null : blob.getBytes(1, (int) blob.length())); + result = (T) (blob == null ? null : blob.getBytes(1, (int) blob.length())); } finally { safeFree(blob); } } else { - return (T) stream.readBytes(); + result = (T) stream.readBytes(); } } else if (type == Clob.class) { - return (T) stream.readClob(); + result = (T) stream.readClob(); } else if (type == Date.class) { - return (T) stream.readDate(); + result = (T) stream.readDate(); } else if (type == Double.class) { - return (T) wasNull(stream, Double.valueOf(stream.readDouble())); + result = (T) wasNull(stream, Double.valueOf(stream.readDouble())); } else if (type == Float.class) { - return (T) wasNull(stream, Float.valueOf(stream.readFloat())); + result = (T) wasNull(stream, Float.valueOf(stream.readFloat())); } else if (type == Integer.class) { - return (T) wasNull(stream, Integer.valueOf(stream.readInt())); + result = (T) wasNull(stream, Integer.valueOf(stream.readInt())); } else if (type == Long.class) { - return (T) wasNull(stream, Long.valueOf(stream.readLong())); + result = (T) wasNull(stream, Long.valueOf(stream.readLong())); } else if (type == Short.class) { - return (T) wasNull(stream, Short.valueOf(stream.readShort())); + result = (T) wasNull(stream, Short.valueOf(stream.readShort())); } else if (type == String.class) { - return (T) stream.readString(); + result = (T) stream.readString(); } else if (type == Time.class) { - return (T) stream.readTime(); + result = (T) stream.readTime(); } else if (type == Timestamp.class) { - return (T) stream.readTimestamp(); + result = (T) stream.readTimestamp(); } else if (type == YearToMonth.class) { String string = stream.readString(); - return (T) (string == null ? null : YearToMonth.valueOf(string)); + result = (T) (string == null ? null : YearToMonth.valueOf(string)); } else if (type == DayToSecond.class) { String string = stream.readString(); - return (T) (string == null ? null : DayToSecond.valueOf(string)); + result = (T) (string == null ? null : DayToSecond.valueOf(string)); } else if (type == UByte.class) { String string = stream.readString(); - return (T) (string == null ? null : UByte.valueOf(string)); + result = (T) (string == null ? null : UByte.valueOf(string)); } else if (type == UShort.class) { String string = stream.readString(); - return (T) (string == null ? null : UShort.valueOf(string)); + result = (T) (string == null ? null : UShort.valueOf(string)); } else if (type == UInteger.class) { String string = stream.readString(); - return (T) (string == null ? null : UInteger.valueOf(string)); + result = (T) (string == null ? null : UInteger.valueOf(string)); } else if (type == ULong.class) { String string = stream.readString(); - return (T) (string == null ? null : ULong.valueOf(string)); + result = (T) (string == null ? null : ULong.valueOf(string)); } else if (type == UUID.class) { - return (T) Convert.convert(stream.readString(), UUID.class); + result = (T) Convert.convert(stream.readString(), UUID.class); } // The type byte[] is handled earlier. byte[][] can be handled here else if (type.isArray()) { - Array result = stream.readArray(); - return (T) (result == null ? null : result.getArray()); + Array array = stream.readArray(); + result = (T) (array == null ? null : array.getArray()); } /* [pro] xx xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxx xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx + xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx x xx [/pro] */ else if (EnumType.class.isAssignableFrom(type)) { - return getEnumType(type, stream.readString()); + result = getEnumType(type, stream.readString()); } else if (UDTRecord.class.isAssignableFrom(type)) { - return (T) stream.readObject(); + result = (T) stream.readObject(); } else { - return (T) unlob(stream.readObject()); + result = (T) unlob(stream.readObject()); } + + return converter.from(result); } - static final void writeToSQLOutput(SQLOutput stream, Field field, T value) throws SQLException { - Class type = field.getType(); - DataType dataType = field.getDataType(); + @SuppressWarnings("unchecked") + static final void writeToSQLOutput(SQLOutput stream, Field field, U v) throws SQLException { + DataType dataType = field.getDataType(); + + // [#650] [#2155] [#3108] Use the Field's Converter before actually binding any value + Converter converter = (Converter) field.getConverter(); + Class type = converter.fromType(); + T value = converter.to(v); if (value == null) { stream.writeObject(null); @@ -2514,7 +2527,17 @@ final class Utils { xx xxxxxxx xx xxx xxxxxx xxxxxx xxxx xxxxxxxxxxxxxxxxxx xxx xxxx xx xxx xx xxxxxxxxxxxxxxxxxxx xxxxx xx xxxxxxxxxxx xxxxxx xx xxxxxx xxxxxxxxxxxxxx xxxxxxxxxxx x xxxxxxxxxxxxxxxx xxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxx xxxxx x xxxxxxxxxxxxxxxxxx + + xx xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxx x + xxxxxxxx xxxxxxxxx x xxx xxxxxxxxxxxxxxxxxxxxx + + xxx xxxx x x xx x x xxxxxxxxxxxxxxxxx xxxx + xxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + + xxxxx x xxxxxxxxxx + x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx x xx [/pro] */ else if (EnumType.class.isAssignableFrom(type)) { @@ -2741,13 +2764,17 @@ final class Utils { xxxxxxxxxxxxxxxx xxxxxx x xxxx x - xxxxxx xx - xx xxxxxx xxxxx xxxxxx xxxx xx xxxxxx xx xxxx xxxxx xx xxxxxx xx xxxxxx xxxxx xxxx xxxx xx xxxx xx xxxxxxx xxxx xx xxxxxx xx xx xxxxxxxxx xxxxxxx xxx xxxxxx xxxxxxx - x x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxx xxxxx x xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxx xxxxxxxxx x xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx + + xxx xxxx x x xx x x xxxxxxxxxxxxx xxxx + xxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + + xxxxxxxxxxxxxxxxxxxxxx + xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x xxxxxx xxxxxxx @@ -2896,93 +2923,99 @@ final class Utils { } @SuppressWarnings("unchecked") - static final T getFromStatement(ExecuteContext ctx, Class type, int index) throws SQLException { + static final U getFromStatement(ExecuteContext ctx, Parameter parameter, int index) throws SQLException { CallableStatement stmt = (CallableStatement) ctx.statement(); + // [#650] [#2155] [#3108] Use the Field's Converter before actually binding any value + Converter converter = (Converter) parameter.getConverter(); + Class type = converter.fromType(); + + T result = null; + if (type == Blob.class) { - return (T) stmt.getBlob(index); + result = (T) stmt.getBlob(index); } else if (type == Boolean.class) { - return (T) wasNull(stmt, Boolean.valueOf(stmt.getBoolean(index))); + result = (T) wasNull(stmt, Boolean.valueOf(stmt.getBoolean(index))); } else if (type == BigInteger.class) { - BigDecimal result = stmt.getBigDecimal(index); - return (T) (result == null ? null : result.toBigInteger()); + BigDecimal d = stmt.getBigDecimal(index); + result = (T) (d == null ? null : d.toBigInteger()); } else if (type == BigDecimal.class) { - return (T) stmt.getBigDecimal(index); + result = (T) stmt.getBigDecimal(index); } else if (type == Byte.class) { - return (T) wasNull(stmt, Byte.valueOf(stmt.getByte(index))); + result = (T) wasNull(stmt, Byte.valueOf(stmt.getByte(index))); } else if (type == byte[].class) { - return (T) stmt.getBytes(index); + result = (T) stmt.getBytes(index); } else if (type == Clob.class) { - return (T) stmt.getClob(index); + result = (T) stmt.getClob(index); } else if (type == Date.class) { - return (T) stmt.getDate(index); + result = (T) stmt.getDate(index); } else if (type == Double.class) { - return (T) wasNull(stmt, Double.valueOf(stmt.getDouble(index))); + result = (T) wasNull(stmt, Double.valueOf(stmt.getDouble(index))); } else if (type == Float.class) { - return (T) wasNull(stmt, Float.valueOf(stmt.getFloat(index))); + result = (T) wasNull(stmt, Float.valueOf(stmt.getFloat(index))); } else if (type == Integer.class) { - return (T) wasNull(stmt, Integer.valueOf(stmt.getInt(index))); + result = (T) wasNull(stmt, Integer.valueOf(stmt.getInt(index))); } else if (type == Long.class) { - return (T) wasNull(stmt, Long.valueOf(stmt.getLong(index))); + result = (T) wasNull(stmt, Long.valueOf(stmt.getLong(index))); } else if (type == Short.class) { - return (T) wasNull(stmt, Short.valueOf(stmt.getShort(index))); + result = (T) wasNull(stmt, Short.valueOf(stmt.getShort(index))); } else if (type == String.class) { - return (T) stmt.getString(index); + result = (T) stmt.getString(index); } else if (type == Time.class) { - return (T) stmt.getTime(index); + result = (T) stmt.getTime(index); } else if (type == Timestamp.class) { - return (T) stmt.getTimestamp(index); + result = (T) stmt.getTimestamp(index); } else if (type == YearToMonth.class) { if (ctx.configuration().dialect() == POSTGRES) { Object object = stmt.getObject(index); - return (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); + result = (T) (object == null ? null : PostgresUtils.toYearToMonth(object)); } else { String string = stmt.getString(index); - return (T) (string == null ? null : YearToMonth.valueOf(string)); + result = (T) (string == null ? null : YearToMonth.valueOf(string)); } } else if (type == DayToSecond.class) { if (ctx.configuration().dialect() == POSTGRES) { Object object = stmt.getObject(index); - return (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); + result = (T) (object == null ? null : PostgresUtils.toDayToSecond(object)); } else { String string = stmt.getString(index); - return (T) (string == null ? null : DayToSecond.valueOf(string)); + result = (T) (string == null ? null : DayToSecond.valueOf(string)); } } else if (type == UByte.class) { String string = stmt.getString(index); - return (T) (string == null ? null : UByte.valueOf(string)); + result = (T) (string == null ? null : UByte.valueOf(string)); } else if (type == UShort.class) { String string = stmt.getString(index); - return (T) (string == null ? null : UShort.valueOf(string)); + result = (T) (string == null ? null : UShort.valueOf(string)); } else if (type == UInteger.class) { String string = stmt.getString(index); - return (T) (string == null ? null : UInteger.valueOf(string)); + result = (T) (string == null ? null : UInteger.valueOf(string)); } else if (type == ULong.class) { String string = stmt.getString(index); - return (T) (string == null ? null : ULong.valueOf(string)); + result = (T) (string == null ? null : ULong.valueOf(string)); } else if (type == UUID.class) { switch (ctx.configuration().dialect().family()) { @@ -2991,7 +3024,8 @@ final class Utils { // java.util.UUID data type case H2: case POSTGRES: { - return (T) stmt.getObject(index); + result = (T) stmt.getObject(index); + break; } /* [pro] xx @@ -3004,38 +3038,44 @@ final class Utils { // Most databases don't have such a type. In this case, jOOQ // simulates the type default: { - return (T) Convert.convert(stmt.getString(index), UUID.class); + result = (T) Convert.convert(stmt.getString(index), UUID.class); + break; } } } // The type byte[] is handled earlier. byte[][] can be handled here else if (type.isArray()) { - return (T) convertArray(stmt.getObject(index), (Class)type); + result = (T) convertArray(stmt.getObject(index), (Class)type); } /* [pro] xx xxxx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxx xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx + xxxxxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxxxx xxxxxx x xx [/pro] */ else if (EnumType.class.isAssignableFrom(type)) { - return getEnumType(type, stmt.getString(index)); + result = getEnumType(type, stmt.getString(index)); } else if (UDTRecord.class.isAssignableFrom(type)) { switch (ctx.configuration().dialect()) { case POSTGRES: - return (T) pgNewUDTRecord(type, stmt.getObject(index)); - } + result = (T) pgNewUDTRecord(type, stmt.getObject(index)); + break; - return (T) stmt.getObject(index, DataTypes.udtRecords()); + default: + result = (T) stmt.getObject(index, DataTypes.udtRecords()); + break; + } } else if (Result.class.isAssignableFrom(type)) { ResultSet nested = (ResultSet) stmt.getObject(index); - return (T) DSL.using(ctx.configuration()).fetch(nested); + result = (T) DSL.using(ctx.configuration()).fetch(nested); } else { - return (T) stmt.getObject(index); + result = (T) stmt.getObject(index); } + + return converter.from(result); } // -------------------------------------------------------------------------