From 880786ff07758c56c261c9e61bb3cea4cceea01e Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Sun, 4 Mar 2012 15:00:17 +0000 Subject: [PATCH] [#1215] Add org.jooq.Converter for custom type mapping [#1216] Overload Record, Result.getValue() and .setValue() methods to accept a Converter --- jOOQ-test/src/org/jooq/test/_/MyEnum.java | 41 ++ .../org/jooq/test/_/MyEnumNumericMapper.java | 69 +++ .../org/jooq/test/_/MyEnumStringMapper.java | 68 +++ .../jooq/test/_/testcases/DataTypeTests.java | 96 ++++ .../jooq/test/_/testcases/GeneralTests.java | 4 +- jOOQ-test/src/org/jooq/test/hsqldb/create.sql | 14 + .../test/hsqldb/generatedclasses/Keys.java | 1 + .../test/hsqldb/generatedclasses/Public.java | 3 +- .../test/hsqldb/generatedclasses/Tables.java | 5 + .../generatedclasses/tables/TMappedTypes.java | 99 ++++ .../tables/records/TMappedTypesRecord.java | 123 +++++ .../src/org/jooq/test/jOOQAbstractTest.java | 5 + .../src/org/jooq/test/jOOQHSQLDBTest.java | 30 ++ jOOQ/src/main/java/org/jooq/Converter.java | 85 ++++ jOOQ/src/main/java/org/jooq/Record.java | 113 +++++ jOOQ/src/main/java/org/jooq/Result.java | 36 ++ jOOQ/src/main/java/org/jooq/ResultQuery.java | 156 +++++- .../jooq/impl/AbstractDelegatingSelect.java | 56 +++ .../java/org/jooq/impl/AbstractRecord.java | 165 ++++--- .../org/jooq/impl/AbstractResultQuery.java | 79 +++- .../java/org/jooq/impl/AbstractStore.java | 60 +-- .../main/java/org/jooq/impl/ResultImpl.java | 16 + .../src/main/java/org/jooq/tools/Convert.java | 447 +++++++++++------- 23 files changed, 1502 insertions(+), 269 deletions(-) create mode 100644 jOOQ-test/src/org/jooq/test/_/MyEnum.java create mode 100644 jOOQ-test/src/org/jooq/test/_/MyEnumNumericMapper.java create mode 100644 jOOQ-test/src/org/jooq/test/_/MyEnumStringMapper.java create mode 100644 jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/TMappedTypes.java create mode 100644 jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/records/TMappedTypesRecord.java create mode 100644 jOOQ/src/main/java/org/jooq/Converter.java diff --git a/jOOQ-test/src/org/jooq/test/_/MyEnum.java b/jOOQ-test/src/org/jooq/test/_/MyEnum.java new file mode 100644 index 0000000000..d54c4a45f9 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/_/MyEnum.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.test._; + +public enum MyEnum { + + A, B, C +} diff --git a/jOOQ-test/src/org/jooq/test/_/MyEnumNumericMapper.java b/jOOQ-test/src/org/jooq/test/_/MyEnumNumericMapper.java new file mode 100644 index 0000000000..f605b3b917 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/_/MyEnumNumericMapper.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.test._; + +import org.jooq.Converter; + + +public class MyEnumNumericMapper implements Converter { + + public static final MyEnumNumericMapper INSTANCE = new MyEnumNumericMapper(); + + @Override + public MyEnum from(Integer t) { + try { + return MyEnum.values()[t]; + } + catch (Exception e) { + return null; + } + } + + @Override + public Integer to(MyEnum u) { + return u == null ? null : u.ordinal(); + } + + @Override + public Class fromType() { + return Integer.class; + } + + @Override + public Class toType() { + return MyEnum.class; + } +} diff --git a/jOOQ-test/src/org/jooq/test/_/MyEnumStringMapper.java b/jOOQ-test/src/org/jooq/test/_/MyEnumStringMapper.java new file mode 100644 index 0000000000..0aaa1b8264 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/_/MyEnumStringMapper.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.test._; + +import org.jooq.Converter; + +public class MyEnumStringMapper implements Converter { + + public static final MyEnumStringMapper INSTANCE = new MyEnumStringMapper(); + + @Override + public MyEnum from(String t) { + try { + return MyEnum.valueOf(t); + } + catch (Exception e) { + return null; + } + } + + @Override + public String to(MyEnum u) { + return u == null ? null : u.name(); + } + + @Override + public Class fromType() { + return String.class; + } + + @Override + public Class toType() { + return MyEnum.class; + } +} diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java index c107b55f43..dfcbe482ec 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/DataTypeTests.java @@ -35,6 +35,7 @@ */ package org.jooq.test._.testcases; +import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -54,9 +55,12 @@ import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.List; +import org.jooq.Converter; import org.jooq.DataType; import org.jooq.InsertSetMoreStep; import org.jooq.Record; @@ -270,6 +274,98 @@ extends BaseTest converter = new Converter() { + @Override + public StringBuilder from(String databaseObject) { + return new StringBuilder("prefix_" + databaseObject); + } + @Override + public String to(StringBuilder userObject) { + return userObject.toString().replace("prefix_", ""); + } + @Override + public Class fromType() { + return String.class; + } + @Override + public Class toType() { + return StringBuilder.class; + } + }; + + List prefixed = asList( + new StringBuilder("prefix_1984"), + new StringBuilder("prefix_Animal Farm"), + new StringBuilder("prefix_O Alquimista"), + new StringBuilder("prefix_Brida")); + + // Check various Result, Record methods + Result result = + create().select(TBook_TITLE()) + .from(TBook()) + .orderBy(TBook_ID()) + .fetch(); + + assertEquals(strings(prefixed), strings(result.getValues(TBook_TITLE(), converter))); + assertEquals(strings(prefixed), strings(result.getValues(TBook_TITLE().getName(), converter))); + assertEquals(strings(prefixed), strings(result.getValues(0, converter))); + + for (int i = 0; i < 4; i++) { + assertEquals(strings(prefixed.subList(i, i + 1)), strings(asList(result.get(i).getValue(TBook_TITLE(), converter)))); + assertEquals(strings(prefixed.subList(i, i + 1)), strings(asList(result.get(i).getValue(TBook_TITLE().getName(), converter)))); + assertEquals(strings(prefixed.subList(i, i + 1)), strings(asList(result.get(i).getValue(0, converter)))); + } + + // Check various fetch methods + assertEquals(strings(prefixed), + strings(create().select(TBook_TITLE()) + .from(TBook()) + .orderBy(TBook_ID()) + .fetch(TBook_TITLE(), converter))); + + assertEquals(strings(prefixed), + strings(create().select(TBook_TITLE()) + .from(TBook()) + .orderBy(TBook_ID()) + .fetch(TBook_TITLE().getName(), converter))); + + assertEquals(strings(prefixed), + strings(create().select(TBook_TITLE()) + .from(TBook()) + .orderBy(TBook_ID()) + .fetch(0, converter))); + + // Check various fetchOne methods + for (int i = 0; i < 4; i++) { + assertEquals(strings(prefixed.subList(i, i + 1)), + strings(asList(create().select(TBook_TITLE()) + .from(TBook()) + .where(TBook_ID().equal(i + 1)) + .fetchOne(TBook_TITLE(), converter)))); + } + + // Check various fetchArray methods + StringBuilder[] array = + create().select(TBook_TITLE()) + .from(TBook()) + .orderBy(TBook_ID()) + .fetchArray(TBook_TITLE(), converter); + + assertEquals(strings(prefixed), strings(asList(array))); + } + + private List strings(List prefixed) { + List result = new ArrayList(); + + for (StringBuilder sb : prefixed) { + result.add(sb.toString()); + } + + return result; + } + @Test public void testCastingToDialectDataType() throws Exception { for (DataType type : getCastableDataTypes()) { diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/GeneralTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/GeneralTests.java index c50cbe293b..d4069e0664 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/GeneralTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/GeneralTests.java @@ -489,7 +489,7 @@ extends BaseTest PK_T_BOOLEANS = createUniqueKey(org.jooq.test.hsqldb.generatedclasses.tables.TBooleans.T_BOOLEANS, org.jooq.test.hsqldb.generatedclasses.tables.TBooleans.T_BOOLEANS.ID); public static final org.jooq.UniqueKey PK_T_DATES = createUniqueKey(org.jooq.test.hsqldb.generatedclasses.tables.TDates.T_DATES, org.jooq.test.hsqldb.generatedclasses.tables.TDates.T_DATES.ID); public static final org.jooq.UniqueKey PK_T_IDENTITY_PK = createUniqueKey(org.jooq.test.hsqldb.generatedclasses.tables.TIdentityPk.T_IDENTITY_PK, org.jooq.test.hsqldb.generatedclasses.tables.TIdentityPk.T_IDENTITY_PK.ID); + public static final org.jooq.UniqueKey PK_T_MAPPED_TYPES = createUniqueKey(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES, org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.ID); public static final org.jooq.UniqueKey PK_T_TRIGGERS = createUniqueKey(org.jooq.test.hsqldb.generatedclasses.tables.TTriggers.T_TRIGGERS, org.jooq.test.hsqldb.generatedclasses.tables.TTriggers.T_TRIGGERS.ID_GENERATED); public static final org.jooq.UniqueKey PK_X_TEST_CASE_64_69 = createUniqueKey(org.jooq.test.hsqldb.generatedclasses.tables.XTestCase_64_69.X_TEST_CASE_64_69, org.jooq.test.hsqldb.generatedclasses.tables.XTestCase_64_69.X_TEST_CASE_64_69.ID); public static final org.jooq.UniqueKey PK_X_TEST_CASE_71 = createUniqueKey(org.jooq.test.hsqldb.generatedclasses.tables.XTestCase_71.X_TEST_CASE_71, org.jooq.test.hsqldb.generatedclasses.tables.XTestCase_71.X_TEST_CASE_71.ID); diff --git a/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Public.java b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Public.java index 8aff0bb2ee..a9c1945913 100644 --- a/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Public.java +++ b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Public.java @@ -10,7 +10,7 @@ package org.jooq.test.hsqldb.generatedclasses; comments = "This class is generated by jOOQ") public class Public extends org.jooq.impl.SchemaImpl { - private static final long serialVersionUID = -459797272; + private static final long serialVersionUID = 65328174; /** * The singleton instance of PUBLIC @@ -55,6 +55,7 @@ public class Public extends org.jooq.impl.SchemaImpl { org.jooq.test.hsqldb.generatedclasses.tables.TDates.T_DATES, org.jooq.test.hsqldb.generatedclasses.tables.TIdentity.T_IDENTITY, org.jooq.test.hsqldb.generatedclasses.tables.TIdentityPk.T_IDENTITY_PK, + org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES, org.jooq.test.hsqldb.generatedclasses.tables.TTriggers.T_TRIGGERS, org.jooq.test.hsqldb.generatedclasses.tables.VAuthor.V_AUTHOR, org.jooq.test.hsqldb.generatedclasses.tables.VBook.V_BOOK, diff --git a/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Tables.java b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Tables.java index 769ec01a92..14fee29abc 100644 --- a/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Tables.java +++ b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/Tables.java @@ -127,6 +127,11 @@ public final class Tables { */ public static org.jooq.test.hsqldb.generatedclasses.tables.TIdentityPk T_IDENTITY_PK = org.jooq.test.hsqldb.generatedclasses.tables.TIdentityPk.T_IDENTITY_PK; + /** + * The table PUBLIC.T_MAPPED_TYPES + */ + public static org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes T_MAPPED_TYPES = org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES; + /** * The table PUBLIC.T_TRIGGERS */ diff --git a/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/TMappedTypes.java b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/TMappedTypes.java new file mode 100644 index 0000000000..27856f4add --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/TMappedTypes.java @@ -0,0 +1,99 @@ +/** + * This class is generated by jOOQ + */ +package org.jooq.test.hsqldb.generatedclasses.tables; + +/** + * This class is generated by jOOQ. + */ +@javax.annotation.Generated(value = {"http://www.jooq.org", "2.0.6"}, + comments = "This class is generated by jOOQ") +public class TMappedTypes extends org.jooq.impl.UpdatableTableImpl { + + private static final long serialVersionUID = -597946068; + + /** + * The singleton instance of PUBLIC.T_MAPPED_TYPES + */ + public static final org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes T_MAPPED_TYPES = new org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes(); + + /** + * The class holding records for this type + */ + private static final java.lang.Class __RECORD_TYPE = org.jooq.test.hsqldb.generatedclasses.tables.records.TMappedTypesRecord.class; + + /** + * The class holding records for this type + */ + @Override + public java.lang.Class getRecordType() { + return __RECORD_TYPE; + } + + /** + * An uncommented item + * + * PRIMARY KEY + */ + public final org.jooq.TableField ID = createField("ID", org.jooq.impl.SQLDataType.INTEGER, this); + + /** + * An uncommented item + */ + public final org.jooq.TableField JAVA_UTIL_DATE = createField("JAVA_UTIL_DATE", org.jooq.impl.SQLDataType.TIMESTAMP, this); + + /** + * An uncommented item + */ + public final org.jooq.TableField JAVA_UTIL_CALENDAR = createField("JAVA_UTIL_CALENDAR", org.jooq.impl.SQLDataType.TIMESTAMP, this); + + /** + * An uncommented item + */ + public final org.jooq.TableField DEFAULT_ENUM_ORDINAL = createField("DEFAULT_ENUM_ORDINAL", org.jooq.impl.SQLDataType.INTEGER, this); + + /** + * An uncommented item + */ + public final org.jooq.TableField DEFAULT_ENUM_NAME = createField("DEFAULT_ENUM_NAME", org.jooq.impl.SQLDataType.VARCHAR, this); + + /** + * An uncommented item + */ + public final org.jooq.TableField CUSTOM_ENUM_NUMERIC = createField("CUSTOM_ENUM_NUMERIC", org.jooq.impl.SQLDataType.INTEGER, this); + + /** + * An uncommented item + */ + public final org.jooq.TableField CUSTOM_ENUM_TEXT = createField("CUSTOM_ENUM_TEXT", org.jooq.impl.SQLDataType.VARCHAR, this); + + /** + * No further instances allowed + */ + private TMappedTypes() { + super("T_MAPPED_TYPES", org.jooq.test.hsqldb.generatedclasses.Public.PUBLIC); + } + + /** + * No further instances allowed + */ + private TMappedTypes(java.lang.String alias) { + super(alias, org.jooq.test.hsqldb.generatedclasses.Public.PUBLIC, org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES); + } + + @Override + public org.jooq.UniqueKey getMainKey() { + return org.jooq.test.hsqldb.generatedclasses.Keys.PK_T_MAPPED_TYPES; + } + + @Override + @SuppressWarnings("unchecked") + public java.util.List> getKeys() { + return java.util.Arrays.>asList(org.jooq.test.hsqldb.generatedclasses.Keys.PK_T_MAPPED_TYPES); + } + + @Override + public org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes as(java.lang.String alias) { + return new org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes(alias); + } +} diff --git a/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/records/TMappedTypesRecord.java b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/records/TMappedTypesRecord.java new file mode 100644 index 0000000000..269ce9af44 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/hsqldb/generatedclasses/tables/records/TMappedTypesRecord.java @@ -0,0 +1,123 @@ +/** + * This class is generated by jOOQ + */ +package org.jooq.test.hsqldb.generatedclasses.tables.records; + +/** + * This class is generated by jOOQ. + */ +@javax.annotation.Generated(value = {"http://www.jooq.org", "2.0.6"}, + comments = "This class is generated by jOOQ") +public class TMappedTypesRecord extends org.jooq.impl.UpdatableRecordImpl { + + private static final long serialVersionUID = 1736776678; + + /** + * An uncommented item + * + * PRIMARY KEY + */ + public void setId(java.lang.Integer value) { + setValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.ID, value); + } + + /** + * An uncommented item + * + * PRIMARY KEY + */ + public java.lang.Integer getId() { + return getValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.ID); + } + + /** + * An uncommented item + */ + public void setJavaUtilDate(java.sql.Timestamp value) { + setValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.JAVA_UTIL_DATE, value); + } + + /** + * An uncommented item + */ + public java.sql.Timestamp getJavaUtilDate() { + return getValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.JAVA_UTIL_DATE); + } + + /** + * An uncommented item + */ + public void setJavaUtilCalendar(java.sql.Timestamp value) { + setValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.JAVA_UTIL_CALENDAR, value); + } + + /** + * An uncommented item + */ + public java.sql.Timestamp getJavaUtilCalendar() { + return getValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.JAVA_UTIL_CALENDAR); + } + + /** + * An uncommented item + */ + public void setDefaultEnumOrdinal(java.lang.Integer value) { + setValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.DEFAULT_ENUM_ORDINAL, value); + } + + /** + * An uncommented item + */ + public java.lang.Integer getDefaultEnumOrdinal() { + return getValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.DEFAULT_ENUM_ORDINAL); + } + + /** + * An uncommented item + */ + public void setDefaultEnumName(java.lang.String value) { + setValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.DEFAULT_ENUM_NAME, value); + } + + /** + * An uncommented item + */ + public java.lang.String getDefaultEnumName() { + return getValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.DEFAULT_ENUM_NAME); + } + + /** + * An uncommented item + */ + public void setCustomEnumNumeric(java.lang.Integer value) { + setValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.CUSTOM_ENUM_NUMERIC, value); + } + + /** + * An uncommented item + */ + public java.lang.Integer getCustomEnumNumeric() { + return getValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.CUSTOM_ENUM_NUMERIC); + } + + /** + * An uncommented item + */ + public void setCustomEnumText(java.lang.String value) { + setValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.CUSTOM_ENUM_TEXT, value); + } + + /** + * An uncommented item + */ + public java.lang.String getCustomEnumText() { + return getValue(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES.CUSTOM_ENUM_TEXT); + } + + /** + * Create a detached TMappedTypesRecord + */ + public TMappedTypesRecord() { + super(org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES); + } +} diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 060669ded2..7bc9f9d806 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -772,6 +772,11 @@ public abstract class jOOQAbstractTest< new DataTypeTests(this).testConversionResult(); } + @Test + public void testCustomConversion() throws Exception { + new DataTypeTests(this).testCustomConversion(); + } + @Test public void testForUpdateClauses() throws Exception { new SelectTests(this).testForUpdateClauses(); diff --git a/jOOQ-test/src/org/jooq/test/jOOQHSQLDBTest.java b/jOOQ-test/src/org/jooq/test/jOOQHSQLDBTest.java index 1b141ce749..51ea9c6603 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQHSQLDBTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQHSQLDBTest.java @@ -36,6 +36,7 @@ package org.jooq.test; +import static junit.framework.Assert.assertEquals; import static org.jooq.test.hsqldb.generatedclasses.Tables.T_639_NUMBERS_TABLE; import static org.jooq.test.hsqldb.generatedclasses.Tables.T_658_REF; import static org.jooq.test.hsqldb.generatedclasses.Tables.T_725_LOB_TEST; @@ -52,6 +53,7 @@ import static org.jooq.test.hsqldb.generatedclasses.Tables.T_TRIGGERS; import static org.jooq.test.hsqldb.generatedclasses.Tables.V_AUTHOR; import static org.jooq.test.hsqldb.generatedclasses.Tables.V_BOOK; import static org.jooq.test.hsqldb.generatedclasses.Tables.V_LIBRARY; +import static org.jooq.test.hsqldb.generatedclasses.tables.TMappedTypes.T_MAPPED_TYPES; import java.math.BigDecimal; import java.math.BigInteger; @@ -68,6 +70,9 @@ import org.jooq.UDTRecord; import org.jooq.UpdatableTable; import org.jooq.conf.Settings; import org.jooq.impl.Factory; +import org.jooq.test._.MyEnum; +import org.jooq.test._.MyEnumNumericMapper; +import org.jooq.test._.MyEnumStringMapper; import org.jooq.test.hsqldb.generatedclasses.PublicFactory; import org.jooq.test.hsqldb.generatedclasses.Routines; import org.jooq.test.hsqldb.generatedclasses.Sequences; @@ -79,6 +84,7 @@ import org.jooq.test.hsqldb.generatedclasses.tables.records.TBookToBookStoreReco import org.jooq.test.hsqldb.generatedclasses.tables.records.TDatesRecord; import org.jooq.test.hsqldb.generatedclasses.tables.records.TIdentityPkRecord; import org.jooq.test.hsqldb.generatedclasses.tables.records.TIdentityRecord; +import org.jooq.test.hsqldb.generatedclasses.tables.records.TMappedTypesRecord; import org.jooq.test.hsqldb.generatedclasses.tables.records.TTriggersRecord; import org.jooq.test.hsqldb.generatedclasses.tables.records.T_639NumbersTableRecord; import org.jooq.test.hsqldb.generatedclasses.tables.records.T_658RefRecord; @@ -92,6 +98,8 @@ import org.jooq.tools.unsigned.ULong; import org.jooq.tools.unsigned.UShort; import org.jooq.util.hsqldb.HSQLDBDataType; +import org.junit.Test; + /** * @author Lukas Eder */ @@ -668,4 +676,26 @@ public class jOOQHSQLDBTest extends jOOQAbstractTest< HSQLDBDataType.VARCHARIGNORECASE, }; } + + @Test + public void testMapper() { + jOOQAbstractTest.reset = false; + + TMappedTypesRecord record; + + // Storing a record using fields from a mapper + record = create().newRecord(T_MAPPED_TYPES); + record.setId(1); + record.setValue(T_MAPPED_TYPES.DEFAULT_ENUM_NAME, MyEnum.A, MyEnumStringMapper.INSTANCE); + record.setValue(T_MAPPED_TYPES.DEFAULT_ENUM_ORDINAL, MyEnum.B, MyEnumNumericMapper.INSTANCE); + + assertEquals(1, record.store()); + + // Retrieving that record again using fields from a mapper + record = create().newRecord(T_MAPPED_TYPES); + record.setId(1); + record.refresh(); + assertEquals(MyEnum.A, record.getValue(T_MAPPED_TYPES.DEFAULT_ENUM_NAME, MyEnumStringMapper.INSTANCE)); + assertEquals(MyEnum.B, record.getValue(T_MAPPED_TYPES.DEFAULT_ENUM_ORDINAL, MyEnumNumericMapper.INSTANCE)); + } } diff --git a/jOOQ/src/main/java/org/jooq/Converter.java b/jOOQ/src/main/java/org/jooq/Converter.java new file mode 100644 index 0000000000..5c3f2a454f --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Converter.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq; + +import org.jooq.impl.SQLDataType; + +/** + * A Converter for data types. + *

+ * A general data type conversion interface that can be provided to jOOQ at + * various places in order to perform custom data type conversion. Conversion is + * directed, this means that the Converter is used + *

    + *
  • to load database types converting them to user types "FROM" the database. + * Hence, {@link #fromType()} is the type as defined in the database.
  • + *
  • to store user types converting them to database types "TO" the database. + * Hence, {@link #toType()} is the user-defined type
  • + *
+ * + * @author Lukas Eder + * @param The database type - i.e. any type available from + * {@link SQLDataType} + * @param The user type + */ +public interface Converter { + + /** + * Convert a database object to a user object + * + * @param databaseObject The database object + * @return The user object + */ + U from(T databaseObject); + + /** + * Convert a user object to a database object + * + * @param userObject The user object + * @return The database object + */ + T to(U userObject); + + /** + * The database type + */ + Class fromType(); + + /** + * The user type + */ + Class toType(); +} diff --git a/jOOQ/src/main/java/org/jooq/Record.java b/jOOQ/src/main/java/org/jooq/Record.java index 846cc500aa..721ba02dbc 100644 --- a/jOOQ/src/main/java/org/jooq/Record.java +++ b/jOOQ/src/main/java/org/jooq/Record.java @@ -946,6 +946,41 @@ public interface Record extends FieldProvider, Store { T getValue(Field field, Class type, T defaultValue) throws IllegalArgumentException, DataTypeException; + /** + * Get a converted value from this Record, providing a field. + * + * @param The database type parameter + * @param The conversion type parameter + * @param field The field + * @param converter The data type converter + * @return The value of a field contained in this record + * @throws IllegalArgumentException If the argument field is not contained + * in {@link #getFields()} + * @throws DataTypeException wrapping any data type conversion exception + * that might have occurred + * @see Convert#convert(Object, Converter) + */ + U getValue(Field field, Converter converter) throws IllegalArgumentException, DataTypeException; + + /** + * Get a converted value from this record, providing a field. + * + * @param The database type parameter + * @param The conversion type parameter + * @param field The field + * @param converter The data type converter + * @param defaultValue The default value instead of null + * @return The value of a field contained in this record, or defaultValue, + * if null + * @throws IllegalArgumentException If the argument field is not contained + * in {@link #getFields()} + * @throws DataTypeException wrapping any data type conversion exception + * that might have occurred + * @see Convert#convert(Object, Converter) + */ + U getValue(Field field, Converter converter, U defaultValue) throws IllegalArgumentException, + DataTypeException; + /** * Get a converted value from this Record, providing a field name. * @@ -979,6 +1014,72 @@ public interface Record extends FieldProvider, Store { T getValue(String fieldName, Class type, T defaultValue) throws IllegalArgumentException, DataTypeException; + /** + * Get a converted value from this Store, providing a field index. + * + * @param The conversion type parameter + * @param index The field's index + * @param converter The data type converter + * @return The value of a field's index contained in this Store + * @throws IllegalArgumentException If the argument index is not contained + * in the Store + * @throws DataTypeException wrapping data type conversion exception that + * might have occurred + * @see Convert#convert(Object, Converter) + */ + U getValue(int index, Converter converter) throws IllegalArgumentException, DataTypeException; + + /** + * Get a converted value from this Store, providing a field index. + * + * @param The conversion type parameter + * @param index The field's index + * @param converter The data type converter + * @param defaultValue The default value instead of null + * @return The value of a field's index contained in this Store, or + * defaultValue, if null + * @throws IllegalArgumentException If the argument index is not contained + * in the Store + * @throws DataTypeException wrapping data type conversion exception that + * might have occurred + * @see Convert#convert(Object, Converter) + */ + U getValue(int index, Converter converter, U defaultValue) throws IllegalArgumentException, + DataTypeException; + + /** + * Get a converted value from this Record, providing a field name. + * + * @param The conversion type parameter + * @param fieldName The field's name + * @param converter The data type converter + * @return The value of a field's name contained in this record + * @throws IllegalArgumentException If the argument fieldName is not + * contained in the record + * @throws DataTypeException wrapping any data type conversion exception + * that might have occurred + * @see Convert#convert(Object, Converter) + */ + U getValue(String fieldName, Converter converter) throws IllegalArgumentException, DataTypeException; + + /** + * Get a converted value from this record, providing a field name. + * + * @param The conversion type parameter + * @param fieldName The field's name + * @param converter The data type converter + * @param defaultValue The default value instead of null + * @return The value of a field's name contained in this record, or + * defaultValue, if null + * @throws IllegalArgumentException If the argument fieldName is not + * contained in the record + * @throws DataTypeException wrapping any data type conversion exception + * that might have occurred + * @see Convert#convert(Object, Converter) + */ + U getValue(String fieldName, Converter converter, U defaultValue) throws IllegalArgumentException, + DataTypeException; + /** * Set a value into this record. * @@ -988,6 +1089,18 @@ public interface Record extends FieldProvider, Store { */ void setValue(Field field, T value); + /** + * Set a value into this record. + * + * @param The generic field parameter + * @param The conversion type parameter + * @param field The field + * @param value The value + * @param converter The converter used to convert value into an + * appropriate type + */ + void setValue(Field field, U value, Converter converter); + /** * Convert this record into an array. *

diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index d8b788d559..b202bcf8b6 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -1252,6 +1252,18 @@ public interface Result extends FieldProvider, List, Attach */ List getValues(Field field, Class type); + /** + * Convenience method to fetch all values for a given field. This is + * especially useful, when selecting only a single field. + * + * @param field The values' field + * @param converter The data type converter used for type conversion + * @return The values + * @see Record#getValue(Field, Converter) + * @see Convert#convert(Object, Converter) + */ + List getValues(Field field, Converter converter); + /** * Convenience method to fetch all values for a given field. This is * especially useful, when selecting only a single field. @@ -1273,6 +1285,18 @@ public interface Result extends FieldProvider, List, Attach */ List getValues(int fieldIndex, Class type); + /** + * Convenience method to fetch all values for a given field. This is + * especially useful, when selecting only a single field. + * + * @param fieldIndex The values' field index + * @param converter The data type converter used for type conversion + * @return The values + * @see Record#getValue(int, Converter) + * @see Convert#convert(Object, Converter) + */ + List getValues(int fieldIndex, Converter converter); + /** * Convenience method to fetch all values for a given field. This is * especially useful, when selecting only a single field. @@ -1294,6 +1318,18 @@ public interface Result extends FieldProvider, List, Attach */ List getValues(String fieldName, Class type); + /** + * Convenience method to fetch all values for a given field. This is + * especially useful, when selecting only a single field. + * + * @param fieldName The values' field name + * @param converter The data type converter used for type conversion + * @return The values + * @see Record#getValue(String, Converter) + * @see Convert#convert(Object, Converter) + */ + List getValues(String fieldName, Converter converter); + /** * Convenience method to fetch all values for a given field. This is * especially useful, when selecting only a single field. diff --git a/jOOQ/src/main/java/org/jooq/ResultQuery.java b/jOOQ/src/main/java/org/jooq/ResultQuery.java index 5c6e6d124a..5266a1c71c 100644 --- a/jOOQ/src/main/java/org/jooq/ResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/ResultQuery.java @@ -171,7 +171,7 @@ public interface ResultQuery extends Query { * result. *

* This is the same as calling {@link #fetch()} and then - * {@link Result#getValues(Field)} + * {@link Result#getValues(Field, Class)} * * @return The resulting values. * @throws DataAccessException if something went wrong executing the query @@ -179,6 +179,19 @@ public interface ResultQuery extends Query { */ List fetch(Field field, Class type) throws DataAccessException; + /** + * Execute the query and return all values for a field from the generated + * result. + *

+ * This is the same as calling {@link #fetch()} and then + * {@link Result#getValues(Field, Converter)} + * + * @return The resulting values. + * @throws DataAccessException if something went wrong executing the query + * @see Record#getValue(Field, Converter) + */ + List fetch(Field field, Converter converter) throws DataAccessException; + /** * Execute the query and return all values for a field index from the * generated result. @@ -204,6 +217,19 @@ public interface ResultQuery extends Query { */ List fetch(int fieldIndex, Class type) throws DataAccessException; + /** + * Execute the query and return all values for a field index from the + * generated result. + *

+ * This is the same as calling {@link #fetch()} and then + * {@link Result#getValues(int, Converter)} + * + * @return The resulting values. + * @throws DataAccessException if something went wrong executing the query + * @see Record#getValue(int, Converter) + */ + List fetch(int fieldIndex, Converter converter) throws DataAccessException; + /** * Execute the query and return all values for a field name from the * generated result. @@ -229,6 +255,19 @@ public interface ResultQuery extends Query { */ List fetch(String fieldName, Class type) throws DataAccessException; + /** + * Execute the query and return all values for a field name from the + * generated result. + *

+ * This is the same as calling {@link #fetch()} and then + * {@link Result#getValues(String, Converter)} + * + * @return The resulting values. + * @throws DataAccessException if something went wrong executing the query + * @see Record#getValue(String, Converter) + */ + List fetch(String fieldName, Converter converter) throws DataAccessException; + /** * Execute the query and return return at most one resulting value for a * field from the generated result. @@ -246,6 +285,40 @@ public interface ResultQuery extends Query { */ T fetchOne(Field field) throws DataAccessException; + /** + * Execute the query and return return at most one resulting value for a + * field from the generated result. + *

+ * This is the same as calling {@link #fetchOne()} and then + * {@link Record#getValue(Field, Class)} + * + * @return The resulting value or null if the query returned no + * records. + * @throws DataAccessException This exception is thrown + *

    + *
  • if something went wrong executing the query
  • if + * the query returned more than one value
  • + *
+ */ + T fetchOne(Field field, Class type) throws DataAccessException; + + /** + * Execute the query and return return at most one resulting value for a + * field from the generated result. + *

+ * This is the same as calling {@link #fetchOne()} and then + * {@link Record#getValue(Field, Converter)} + * + * @return The resulting value or null if the query returned no + * records. + * @throws DataAccessException This exception is thrown + *

    + *
  • if something went wrong executing the query
  • if + * the query returned more than one value
  • + *
+ */ + U fetchOne(Field field, Converter converter) throws DataAccessException; + /** * Execute the query and return return at most one resulting value for a * field index from the generated result. @@ -280,6 +353,23 @@ public interface ResultQuery extends Query { */ T fetchOne(int fieldIndex, Class type) throws DataAccessException; + /** + * Execute the query and return return at most one resulting value for a + * field index from the generated result. + *

+ * This is the same as calling {@link #fetchOne()} and then + * {@link Record#getValue(int, Converter)} + * + * @return The resulting value or null if the query returned no + * records. + * @throws DataAccessException This exception is thrown + *

    + *
  • if something went wrong executing the query
  • if + * the query returned more than one value
  • + *
+ */ + U fetchOne(int fieldIndex, Converter converter) throws DataAccessException; + /** * Execute the query and return return at most one resulting value for a * field name from the generated result. @@ -314,6 +404,23 @@ public interface ResultQuery extends Query { */ T fetchOne(String fieldName, Class type) throws DataAccessException; + /** + * Execute the query and return return at most one resulting value for a + * field name from the generated result. + *

+ * This is the same as calling {@link #fetchOne()} and then + * {@link Record#getValue(String, Converter)} + * + * @return The resulting value or null if the query returned no + * records. + * @throws DataAccessException This exception is thrown + *

    + *
  • if something went wrong executing the query
  • if + * the query returned more than one value
  • + *
+ */ + U fetchOne(String fieldName, Converter converter) throws DataAccessException; + /** * Execute the query and return at most one resulting record. * @@ -440,6 +547,17 @@ public interface ResultQuery extends Query { */ T[] fetchArray(int fieldIndex, Class type) throws DataAccessException; + /** + * Execute the query and return all values for a field index from the + * generated result. + *

+ * You can access data like this + *

query.fetchArray(fieldIndex)[recordIndex]
+ * + * @return The resulting values. + */ + U[] fetchArray(int fieldIndex, Converter converter) throws DataAccessException; + /** * Execute the query and return all values for a field name from the * generated result. @@ -452,6 +570,18 @@ public interface ResultQuery extends Query { */ Object[] fetchArray(String fieldName) throws DataAccessException; + /** + * Execute the query and return all values for a field name from the + * generated result. + *

+ * You can access data like this + *

query.fetchArray(fieldName)[recordIndex]
+ * + * @return The resulting values. + * @throws DataAccessException if something went wrong executing the query + */ + U[] fetchArray(String fieldName, Converter converter) throws DataAccessException; + /** * Execute the query and return all values for a field name from the * generated result. @@ -476,6 +606,30 @@ public interface ResultQuery extends Query { */ T[] fetchArray(Field field) throws DataAccessException; + /** + * Execute the query and return all values for a field from the generated + * result. + *

+ * You can access data like this + *

query.fetchArray(field)[recordIndex]
+ * + * @return The resulting values. + * @throws DataAccessException if something went wrong executing the query + */ + T[] fetchArray(Field field, Class type) throws DataAccessException; + + /** + * Execute the query and return all values for a field from the generated + * result. + *

+ * You can access data like this + *

query.fetchArray(field)[recordIndex]
+ * + * @return The resulting values. + * @throws DataAccessException if something went wrong executing the query + */ + U[] fetchArray(Field field, Converter converter) throws DataAccessException; + /** * Execute the query and return at most one resulting record as an array *

diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java index b4a63704b2..16745c5782 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java @@ -40,6 +40,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; +import org.jooq.Converter; import org.jooq.Cursor; import org.jooq.Field; import org.jooq.FutureResult; @@ -126,6 +127,11 @@ abstract class AbstractDelegatingSelect return getDelegate().fetch(field, type); } + @Override + public final List fetch(Field field, Converter converter) { + return getDelegate().fetch(field, converter); + } + @Override public final List fetch(int fieldIndex) { return getDelegate().fetch(fieldIndex); @@ -136,6 +142,11 @@ abstract class AbstractDelegatingSelect return getDelegate().fetch(fieldIndex, type); } + @Override + public final List fetch(int fieldIndex, Converter converter) { + return getDelegate().fetch(fieldIndex, converter); + } + @Override public final List fetch(String fieldName) { return getDelegate().fetch(fieldName); @@ -146,11 +157,26 @@ abstract class AbstractDelegatingSelect return getDelegate().fetch(fieldName, type); } + @Override + public final List fetch(String fieldName, Converter converter) { + return getDelegate().fetch(fieldName, converter); + } + @Override public final T fetchOne(Field field) { return getDelegate().fetchOne(field); } + @Override + public final T fetchOne(Field field, Class type) { + return getDelegate().fetchOne(field, type); + } + + @Override + public final U fetchOne(Field field, Converter converter) { + return getDelegate().fetchOne(field, converter); + } + @Override public final Object fetchOne(int fieldIndex) { return getDelegate().fetchOne(fieldIndex); @@ -161,6 +187,11 @@ abstract class AbstractDelegatingSelect return getDelegate().fetchOne(fieldIndex, type); } + @Override + public final U fetchOne(int fieldIndex, Converter converter) { + return getDelegate().fetchOne(fieldIndex, converter); + } + @Override public final Object fetchOne(String fieldName) { return getDelegate().fetchOne(fieldName); @@ -171,6 +202,11 @@ abstract class AbstractDelegatingSelect return getDelegate().fetchOne(fieldName, type); } + @Override + public final U fetchOne(String fieldName, Converter converter) { + return getDelegate().fetchOne(fieldName, converter); + } + @Override public final R fetchOne() { return getDelegate().fetchOne(); @@ -216,6 +252,11 @@ abstract class AbstractDelegatingSelect return getDelegate().fetchArray(fieldIndex, type); } + @Override + public final U[] fetchArray(int fieldIndex, Converter converter) { + return getDelegate().fetchArray(fieldIndex, converter); + } + @Override public final Object[] fetchArray(String fieldName) { return getDelegate().fetchArray(fieldName); @@ -226,11 +267,26 @@ abstract class AbstractDelegatingSelect return getDelegate().fetchArray(fieldName, type); } + @Override + public final U[] fetchArray(String fieldName, Converter converter) { + return getDelegate().fetchArray(fieldName, converter); + } + @Override public final T[] fetchArray(Field field) { return getDelegate().fetchArray(field); } + @Override + public final T[] fetchArray(Field field, Class type) { + return getDelegate().fetchArray(field, type); + } + + @Override + public final U[] fetchArray(Field field, Converter converter) { + return getDelegate().fetchArray(field, converter); + } + @Override public final Object[] fetchOneArray() { return getDelegate().fetchOneArray(); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index fd9ec6001b..8ad36e4067 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -58,6 +58,7 @@ import java.util.List; import org.jooq.ArrayRecord; import org.jooq.Attachable; +import org.jooq.Converter; import org.jooq.Field; import org.jooq.FieldProvider; import org.jooq.Record; @@ -154,12 +155,12 @@ abstract class AbstractRecord extends AbstractStore implements Record { } @Override - public final T getValue(Field field) throws IllegalArgumentException { + public final T getValue(Field field) { return getValue0(field).getValue(); } @Override - public final T getValue(Field field, T defaultValue) throws IllegalArgumentException { + public final T getValue(Field field, T defaultValue) { return getValue0(field).getValue(defaultValue); } @@ -192,6 +193,11 @@ abstract class AbstractRecord extends AbstractStore implements Record { } } + @Override + public final void setValue(Field field, U value, Converter converter) { + setValue(field, converter.to(value)); + } + final void setValue(Field field, Value value) { getValues()[getIndex(field)] = value; } @@ -209,321 +215,354 @@ abstract class AbstractRecord extends AbstractStore implements Record { } @Override - public final String getValueAsString(Field field) throws IllegalArgumentException { + public final String getValueAsString(Field field) { return getValueAsString(getIndex(field)); } @Override - public final String getValueAsString(Field field, String defaultValue) throws IllegalArgumentException { + public final String getValueAsString(Field field, String defaultValue) { return getValueAsString(getIndex(field), defaultValue); } @Override - public final Byte getValueAsByte(Field field) throws IllegalArgumentException { + public final Byte getValueAsByte(Field field) { return getValueAsByte(getIndex(field)); } @Override - public final Byte getValueAsByte(Field field, Byte defaultValue) throws IllegalArgumentException { + public final Byte getValueAsByte(Field field, Byte defaultValue) { return getValueAsByte(getIndex(field), defaultValue); } @Override - public final Short getValueAsShort(Field field) throws IllegalArgumentException { + public final Short getValueAsShort(Field field) { return getValueAsShort(getIndex(field)); } @Override - public final Short getValueAsShort(Field field, Short defaultValue) throws IllegalArgumentException { + public final Short getValueAsShort(Field field, Short defaultValue) { return getValueAsShort(getIndex(field), defaultValue); } @Override - public final Integer getValueAsInteger(Field field) throws IllegalArgumentException { + public final Integer getValueAsInteger(Field field) { return getValueAsInteger(getIndex(field)); } @Override - public final Integer getValueAsInteger(Field field, Integer defaultValue) throws IllegalArgumentException { + public final Integer getValueAsInteger(Field field, Integer defaultValue) { return getValueAsInteger(getIndex(field), defaultValue); } @Override - public final Long getValueAsLong(Field field) throws IllegalArgumentException { + public final Long getValueAsLong(Field field) { return getValueAsLong(getIndex(field)); } @Override - public final Long getValueAsLong(Field field, Long defaultValue) throws IllegalArgumentException { + public final Long getValueAsLong(Field field, Long defaultValue) { return getValueAsLong(getIndex(field), defaultValue); } @Override - public final BigInteger getValueAsBigInteger(Field field) throws IllegalArgumentException { + public final BigInteger getValueAsBigInteger(Field field) { return getValueAsBigInteger(getIndex(field)); } @Override public final BigInteger getValueAsBigInteger(Field field, BigInteger defaultValue) - throws IllegalArgumentException { + { return getValueAsBigInteger(getIndex(field), defaultValue); } @Override - public final Float getValueAsFloat(Field field) throws IllegalArgumentException { + public final Float getValueAsFloat(Field field) { return getValueAsFloat(getIndex(field)); } @Override - public final Float getValueAsFloat(Field field, Float defaultValue) throws IllegalArgumentException { + public final Float getValueAsFloat(Field field, Float defaultValue) { return getValueAsFloat(getIndex(field), defaultValue); } @Override - public final Double getValueAsDouble(Field field) throws IllegalArgumentException { + public final Double getValueAsDouble(Field field) { return getValueAsDouble(getIndex(field)); } @Override - public final Double getValueAsDouble(Field field, Double defaultValue) throws IllegalArgumentException { + public final Double getValueAsDouble(Field field, Double defaultValue) { return getValueAsDouble(getIndex(field), defaultValue); } @Override - public final BigDecimal getValueAsBigDecimal(Field field) throws IllegalArgumentException { + public final BigDecimal getValueAsBigDecimal(Field field) { return getValueAsBigDecimal(getIndex(field)); } @Override public final BigDecimal getValueAsBigDecimal(Field field, BigDecimal defaultValue) - throws IllegalArgumentException { + { return getValueAsBigDecimal(getIndex(field), defaultValue); } @Override - public final Boolean getValueAsBoolean(Field field) throws IllegalArgumentException { + public final Boolean getValueAsBoolean(Field field) { return getValueAsBoolean(getIndex(field)); } @Override - public final Boolean getValueAsBoolean(Field field, Boolean defaultValue) throws IllegalArgumentException { + public final Boolean getValueAsBoolean(Field field, Boolean defaultValue) { return getValueAsBoolean(getIndex(field), defaultValue); } @Override - public final Timestamp getValueAsTimestamp(Field field) throws IllegalArgumentException { + public final Timestamp getValueAsTimestamp(Field field) { return getValueAsTimestamp(getIndex(field)); } @Override - public final Timestamp getValueAsTimestamp(Field field, Timestamp defaultValue) throws IllegalArgumentException { + public final Timestamp getValueAsTimestamp(Field field, Timestamp defaultValue) { return getValueAsTimestamp(getIndex(field), defaultValue); } @Override - public final Date getValueAsDate(Field field) throws IllegalArgumentException { + public final Date getValueAsDate(Field field) { return getValueAsDate(getIndex(field)); } @Override - public final Date getValueAsDate(Field field, Date defaultValue) throws IllegalArgumentException { + public final Date getValueAsDate(Field field, Date defaultValue) { return getValueAsDate(getIndex(field), defaultValue); } @Override - public final Time getValueAsTime(Field field) throws IllegalArgumentException { + public final Time getValueAsTime(Field field) { return getValueAsTime(getIndex(field)); } @Override - public final Time getValueAsTime(Field field, Time defaultValue) throws IllegalArgumentException { + public final Time getValueAsTime(Field field, Time defaultValue) { return getValueAsTime(getIndex(field), defaultValue); } @Override - public final Object getValue(int index) throws IllegalArgumentException { + public final Object getValue(int index) { return getValue(getField(index)); } @Override - public final Object getValue(String fieldName) throws IllegalArgumentException { + public final Object getValue(String fieldName) { return getValue(getField(fieldName)); } @SuppressWarnings("unchecked") @Override - public final Object getValue(String fieldName, Object defaultValue) throws IllegalArgumentException { + public final Object getValue(String fieldName, Object defaultValue) { return getValue((Field) getField(fieldName), defaultValue); } @Override - public final , T> T[] getValueAsArray(Field field) throws IllegalArgumentException { + public final , T> T[] getValueAsArray(Field field) { A result = getValue(field); return result == null ? null : result.get(); } @Override public final , T> T[] getValueAsArray(Field field, T[] defaultValue) - throws IllegalArgumentException { + { final T[] result = getValueAsArray(field); return result == null ? defaultValue : result; } @Override - public final String getValueAsString(String fieldName) throws IllegalArgumentException { + public final String getValueAsString(String fieldName) { return getValueAsString(getField(fieldName)); } @Override - public final String getValueAsString(String fieldName, String defaultValue) throws IllegalArgumentException { + public final String getValueAsString(String fieldName, String defaultValue) { return getValueAsString(getField(fieldName), defaultValue); } @Override - public final Byte getValueAsByte(String fieldName) throws IllegalArgumentException { + public final Byte getValueAsByte(String fieldName) { return getValueAsByte(getField(fieldName)); } @Override - public final Byte getValueAsByte(String fieldName, Byte defaultValue) throws IllegalArgumentException { + public final Byte getValueAsByte(String fieldName, Byte defaultValue) { return getValueAsByte(getField(fieldName), defaultValue); } @Override - public final Short getValueAsShort(String fieldName) throws IllegalArgumentException { + public final Short getValueAsShort(String fieldName) { return getValueAsShort(getField(fieldName)); } @Override - public final Short getValueAsShort(String fieldName, Short defaultValue) throws IllegalArgumentException { + public final Short getValueAsShort(String fieldName, Short defaultValue) { return getValueAsShort(getField(fieldName), defaultValue); } @Override - public final Integer getValueAsInteger(String fieldName) throws IllegalArgumentException { + public final Integer getValueAsInteger(String fieldName) { return getValueAsInteger(getField(fieldName)); } @Override - public final Integer getValueAsInteger(String fieldName, Integer defaultValue) throws IllegalArgumentException { + public final Integer getValueAsInteger(String fieldName, Integer defaultValue) { return getValueAsInteger(getField(fieldName), defaultValue); } @Override - public final Long getValueAsLong(String fieldName) throws IllegalArgumentException { + public final Long getValueAsLong(String fieldName) { return getValueAsLong(getField(fieldName)); } @Override - public final Long getValueAsLong(String fieldName, Long defaultValue) throws IllegalArgumentException { + public final Long getValueAsLong(String fieldName, Long defaultValue) { return getValueAsLong(getField(fieldName), defaultValue); } @Override - public final BigInteger getValueAsBigInteger(String fieldName) throws IllegalArgumentException { + public final BigInteger getValueAsBigInteger(String fieldName) { return getValueAsBigInteger(getField(fieldName)); } @Override public final BigInteger getValueAsBigInteger(String fieldName, BigInteger defaultValue) - throws IllegalArgumentException { + { return getValueAsBigInteger(getField(fieldName), defaultValue); } @Override - public final Float getValueAsFloat(String fieldName) throws IllegalArgumentException { + public final Float getValueAsFloat(String fieldName) { return getValueAsFloat(getField(fieldName)); } @Override - public final Float getValueAsFloat(String fieldName, Float defaultValue) throws IllegalArgumentException { + public final Float getValueAsFloat(String fieldName, Float defaultValue) { return getValueAsFloat(getField(fieldName), defaultValue); } @Override - public final Double getValueAsDouble(String fieldName) throws IllegalArgumentException { + public final Double getValueAsDouble(String fieldName) { return getValueAsDouble(getField(fieldName)); } @Override - public final Double getValueAsDouble(String fieldName, Double defaultValue) throws IllegalArgumentException { + public final Double getValueAsDouble(String fieldName, Double defaultValue) { return getValueAsDouble(getField(fieldName), defaultValue); } @Override - public final BigDecimal getValueAsBigDecimal(String fieldName) throws IllegalArgumentException { + public final BigDecimal getValueAsBigDecimal(String fieldName) { return getValueAsBigDecimal(getField(fieldName)); } @Override public final BigDecimal getValueAsBigDecimal(String fieldName, BigDecimal defaultValue) - throws IllegalArgumentException { + { return getValueAsBigDecimal(getField(fieldName), defaultValue); } @Override - public final Boolean getValueAsBoolean(String fieldName) throws IllegalArgumentException { + public final Boolean getValueAsBoolean(String fieldName) { return getValueAsBoolean(getField(fieldName)); } @Override - public final Boolean getValueAsBoolean(String fieldName, Boolean defaultValue) throws IllegalArgumentException { + public final Boolean getValueAsBoolean(String fieldName, Boolean defaultValue) { return getValueAsBoolean(getField(fieldName), defaultValue); } @Override - public final Timestamp getValueAsTimestamp(String fieldName) throws IllegalArgumentException { + public final Timestamp getValueAsTimestamp(String fieldName) { return getValueAsTimestamp(getField(fieldName)); } @Override public final Timestamp getValueAsTimestamp(String fieldName, Timestamp defaultValue) - throws IllegalArgumentException { + { return getValueAsTimestamp(getField(fieldName), defaultValue); } @Override - public final Date getValueAsDate(String fieldName) throws IllegalArgumentException { + public final Date getValueAsDate(String fieldName) { return getValueAsDate(getField(fieldName)); } @Override - public final Date getValueAsDate(String fieldName, Date defaultValue) throws IllegalArgumentException { + public final Date getValueAsDate(String fieldName, Date defaultValue) { return getValueAsDate(getField(fieldName), defaultValue); } @Override - public final Time getValueAsTime(String fieldName) throws IllegalArgumentException { + public final Time getValueAsTime(String fieldName) { return getValueAsTime(getField(fieldName)); } @Override - public final Time getValueAsTime(String fieldName, Time defaultValue) throws IllegalArgumentException { + public final Time getValueAsTime(String fieldName, Time defaultValue) { return getValueAsTime(getField(fieldName), defaultValue); } @Override - public final T getValue(Field field, Class type) throws IllegalArgumentException { + public final T getValue(Field field, Class type) { return Convert.convert(getValue(field), type); } @Override - public final T getValue(Field field, Class type, T defaultValue) throws IllegalArgumentException { + public final T getValue(Field field, Class type, T defaultValue) { final T result = getValue(field, type); return result == null ? defaultValue : result; } @Override - public final T getValue(String fieldName, Class type) throws IllegalArgumentException { + public final T getValue(String fieldName, Class type) { return Convert.convert(getValue(fieldName), type); } @Override - public final Z getValue(String fieldName, Class type, Z defaultValue) throws IllegalArgumentException { + public final Z getValue(String fieldName, Class type, Z defaultValue) { final Z result = getValue(fieldName, type); return result == null ? defaultValue : result; } + @Override + public final U getValue(Field field, Converter converter) { + return converter.from(getValue(field)); + } + + @Override + public final U getValue(Field field, Converter converter, U defaultValue) { + final U result = getValue(field, converter); + return result == null ? defaultValue : result; + } + + @Override + public final U getValue(int index, Converter converter) { + return Convert.convert(getValue(index), converter); + } + + @Override + public final U getValue(int index, Converter converter, U defaultValue) { + final U result = getValue(index, converter); + return result == null ? defaultValue : result; + } + + @Override + public final U getValue(String fieldName, Converter converter) { + return Convert.convert(getValue(fieldName), converter); + } + + @Override + public final U getValue(String fieldName, Converter converter, U defaultValue) { + final U result = getValue(fieldName, converter); + return result == null ? defaultValue : result; + } + @Override public final Object[] intoArray() { return into(Object[].class); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index a64d3098a1..92d2a35cf6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -52,6 +52,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import org.jooq.Configuration; +import org.jooq.Converter; import org.jooq.Cursor; import org.jooq.ExecuteContext; import org.jooq.ExecuteListener; @@ -64,7 +65,6 @@ import org.jooq.Result; import org.jooq.ResultQuery; import org.jooq.SQLDialect; import org.jooq.Table; -import org.jooq.exception.DataAccessException; import org.jooq.exception.DataTypeException; import org.jooq.exception.InvalidResultException; import org.jooq.tools.Convert; @@ -207,7 +207,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple } @Override - public final ResultSet fetchResultSet() throws DataAccessException { + public final ResultSet fetchResultSet() { return fetchLazy().resultSet(); } @@ -217,7 +217,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple } @Override - public final Cursor fetchLazy(int fetchSize) throws DataAccessException { + public final Cursor fetchLazy(int fetchSize) { lazy = true; size = fetchSize; @@ -247,10 +247,15 @@ abstract class AbstractResultQuery extends AbstractQuery imple } @Override - public final List fetch(Field field, Class type) throws DataAccessException { + public final List fetch(Field field, Class type) { return fetch().getValues(field, type); } + @Override + public final List fetch(Field field, Converter converter) { + return fetch().getValues(field, converter); + } + @Override public final List fetch(int fieldIndex) { return fetch().getValues(fieldIndex); @@ -261,6 +266,11 @@ abstract class AbstractResultQuery extends AbstractQuery imple return fetch().getValues(fieldIndex, type); } + @Override + public final List fetch(int fieldIndex, Converter converter) { + return fetch().getValues(fieldIndex, converter); + } + @Override public final List fetch(String fieldName) { return fetch().getValues(fieldName); @@ -271,12 +281,27 @@ abstract class AbstractResultQuery extends AbstractQuery imple return fetch().getValues(fieldName, type); } + @Override + public final List fetch(String fieldName, Converter converter) { + return fetch().getValues(fieldName, converter); + } + @Override public final T fetchOne(Field field) { R record = fetchOne(); return record == null ? null : record.getValue(field); } + @Override + public final T fetchOne(Field field, Class type) { + return Convert.convert(fetchOne(field), type); + } + + @Override + public final U fetchOne(Field field, Converter converter) { + return Convert.convert(fetchOne(field), converter); + } + @Override public final Object fetchOne(int fieldIndex) { R record = fetchOne(); @@ -288,6 +313,11 @@ abstract class AbstractResultQuery extends AbstractQuery imple return Convert.convert(fetchOne(fieldIndex), type); } + @Override + public final U fetchOne(int fieldIndex, Converter converter) { + return Convert.convert(fetchOne(fieldIndex), converter); + } + @Override public final Object fetchOne(String fieldName) { R record = fetchOne(); @@ -299,6 +329,11 @@ abstract class AbstractResultQuery extends AbstractQuery imple return Convert.convert(fetchOne(fieldName), type); } + @Override + public final U fetchOne(String fieldName, Converter converter) { + return Convert.convert(fetchOne(fieldName), converter); + } + @Override public final R fetchOne() { Result r = fetch(); @@ -393,6 +428,12 @@ abstract class AbstractResultQuery extends AbstractQuery imple return (T[]) Convert.convertArray(fetchArray(fieldIndex), type); } + @SuppressWarnings("cast") + @Override + public final U[] fetchArray(int fieldIndex, Converter converter) { + return (U[]) Convert.convertArray(fetchArray(fieldIndex), converter); + } + @Override public final Object[] fetchArray(String fieldName) { return fetch(fieldName).toArray(); @@ -404,12 +445,40 @@ abstract class AbstractResultQuery extends AbstractQuery imple return (T[]) Convert.convertArray(fetchArray(fieldName), type); } + @SuppressWarnings("cast") + @Override + public final U[] fetchArray(String fieldName, Converter converter) { + return (U[]) Convert.convertArray(fetchArray(fieldName), converter); + } + @SuppressWarnings("unchecked") @Override public final T[] fetchArray(Field field) { return fetch(field).toArray((T[]) Array.newInstance(field.getType(), 0)); } + @SuppressWarnings("unchecked") + @Override + public final T[] fetchArray(Field field, Class type) { + return (T[]) Convert.convertArray(fetchArray(field), type); + } + + @SuppressWarnings("cast") + @Override + public final U[] fetchArray(Field field, Converter converter) { + return (U[]) Convert.convertArray(fetchArray(field), converter); + } + + /** + * Subclasses may override this method + *

+ * {@inheritDoc} + */ + @Override + public Class getRecordType() { + return null; + } + @Override public final Object[] fetchOneArray() { return fetchOne().intoArray(); @@ -421,7 +490,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple } @Override - public final Result fetchInto(Table table) throws DataAccessException { + public final Result fetchInto(Table table) { return fetch().into(table); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java index fb1651388f..9236c50a8f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java @@ -72,7 +72,7 @@ abstract class AbstractStore implements Store, AttachableInternal { // ------------------------------------------------------------------------- @Override - public final I internalAPI(Class internalType) throws ClassCastException { + public final I internalAPI(Class internalType) { return internalType.cast(this); } @@ -98,161 +98,161 @@ abstract class AbstractStore implements Store, AttachableInternal { // ------------------------------------------------------------------------- @Override - public final T getValue(int index, T defaultValue) throws IllegalArgumentException { + public final T getValue(int index, T defaultValue) { final T result = getValue(index); return result == null ? defaultValue : result; } @Override - public final BigDecimal getValueAsBigDecimal(int index) throws IllegalArgumentException { + public final BigDecimal getValueAsBigDecimal(int index) { return Convert.convert(getValue(index), BigDecimal.class); } @Override - public final BigDecimal getValueAsBigDecimal(int index, BigDecimal defaultValue) throws IllegalArgumentException { + public final BigDecimal getValueAsBigDecimal(int index, BigDecimal defaultValue) { final BigDecimal result = getValueAsBigDecimal(index); return result == null ? defaultValue : result; } @Override - public final Boolean getValueAsBoolean(int index) throws IllegalArgumentException { + public final Boolean getValueAsBoolean(int index) { return Convert.convert(getValue(index), Boolean.class); } @Override - public final Boolean getValueAsBoolean(int index, Boolean defaultValue) throws IllegalArgumentException { + public final Boolean getValueAsBoolean(int index, Boolean defaultValue) { final Boolean result = getValueAsBoolean(index); return result == null ? defaultValue : result; } @Override - public final BigInteger getValueAsBigInteger(int index) throws IllegalArgumentException { + public final BigInteger getValueAsBigInteger(int index) { return Convert.convert(getValue(index), BigInteger.class); } @Override - public final BigInteger getValueAsBigInteger(int index, BigInteger defaultValue) throws IllegalArgumentException { + public final BigInteger getValueAsBigInteger(int index, BigInteger defaultValue) { final BigInteger result = getValueAsBigInteger(index); return result == null ? defaultValue : result; } @Override - public final Byte getValueAsByte(int index) throws IllegalArgumentException { + public final Byte getValueAsByte(int index) { return Convert.convert(getValue(index), Byte.class); } @Override - public final Byte getValueAsByte(int index, Byte defaultValue) throws IllegalArgumentException { + public final Byte getValueAsByte(int index, Byte defaultValue) { final Byte result = getValueAsByte(index); return result == null ? defaultValue : result; } @Override - public final Date getValueAsDate(int index) throws IllegalArgumentException { + public final Date getValueAsDate(int index) { return Convert.convert(getValue(index), Date.class); } @Override - public final Date getValueAsDate(int index, Date defaultValue) throws IllegalArgumentException { + public final Date getValueAsDate(int index, Date defaultValue) { final Date result = getValueAsDate(index); return result == null ? defaultValue : result; } @Override - public final Double getValueAsDouble(int index) throws IllegalArgumentException { + public final Double getValueAsDouble(int index) { return Convert.convert(getValue(index), Double.class); } @Override - public final Double getValueAsDouble(int index, Double defaultValue) throws IllegalArgumentException { + public final Double getValueAsDouble(int index, Double defaultValue) { final Double result = getValueAsDouble(index); return result == null ? defaultValue : result; } @Override - public final Float getValueAsFloat(int index) throws IllegalArgumentException { + public final Float getValueAsFloat(int index) { return Convert.convert(getValue(index), Float.class); } @Override - public final Float getValueAsFloat(int index, Float defaultValue) throws IllegalArgumentException { + public final Float getValueAsFloat(int index, Float defaultValue) { final Float result = getValueAsFloat(index); return result == null ? defaultValue : result; } @Override - public final Integer getValueAsInteger(int index) throws IllegalArgumentException { + public final Integer getValueAsInteger(int index) { return Convert.convert(getValue(index), Integer.class); } @Override - public final Integer getValueAsInteger(int index, Integer defaultValue) throws IllegalArgumentException { + public final Integer getValueAsInteger(int index, Integer defaultValue) { final Integer result = getValueAsInteger(index); return result == null ? defaultValue : result; } @Override - public final Long getValueAsLong(int index) throws IllegalArgumentException { + public final Long getValueAsLong(int index) { return Convert.convert(getValue(index), Long.class); } @Override - public final Long getValueAsLong(int index, Long defaultValue) throws IllegalArgumentException { + public final Long getValueAsLong(int index, Long defaultValue) { final Long result = getValueAsLong(index); return result == null ? defaultValue : result; } @Override - public final Short getValueAsShort(int index) throws IllegalArgumentException { + public final Short getValueAsShort(int index) { return Convert.convert(getValue(index), Short.class); } @Override - public final Short getValueAsShort(int index, Short defaultValue) throws IllegalArgumentException { + public final Short getValueAsShort(int index, Short defaultValue) { final Short result = getValueAsShort(index); return result == null ? defaultValue : result; } @Override - public final String getValueAsString(int index) throws IllegalArgumentException { + public final String getValueAsString(int index) { return Convert.convert(getValue(index), String.class); } @Override - public final String getValueAsString(int index, String defaultValue) throws IllegalArgumentException { + public final String getValueAsString(int index, String defaultValue) { final String result = getValueAsString(index); return result == null ? defaultValue : result; } @Override - public final Time getValueAsTime(int index) throws IllegalArgumentException { + public final Time getValueAsTime(int index) { return Convert.convert(getValue(index), Time.class); } @Override - public final Time getValueAsTime(int index, Time defaultValue) throws IllegalArgumentException { + public final Time getValueAsTime(int index, Time defaultValue) { final Time result = getValueAsTime(index); return result == null ? defaultValue : result; } @Override - public final Timestamp getValueAsTimestamp(int index) throws IllegalArgumentException { + public final Timestamp getValueAsTimestamp(int index) { return Convert.convert(getValue(index), Timestamp.class); } @Override - public final Timestamp getValueAsTimestamp(int index, Timestamp defaultValue) throws IllegalArgumentException { + public final Timestamp getValueAsTimestamp(int index, Timestamp defaultValue) { final Timestamp result = getValueAsTimestamp(index); return result == null ? defaultValue : result; } @Override - public final Z getValue(int index, Class type) throws IllegalArgumentException { + public final Z getValue(int index, Class type) { return Convert.convert(getValue(index), type); } @Override - public final Z getValue(int index, Class type, Z defaultValue) throws IllegalArgumentException { + public final Z getValue(int index, Class type, Z defaultValue) { final Z result = getValue(index, type); return result == null ? defaultValue : result; } diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java index 47ee28f6b4..b4e68eba57 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java @@ -65,6 +65,7 @@ import org.jooq.ArrayRecord; import org.jooq.Attachable; import org.jooq.AttachableInternal; import org.jooq.Configuration; +import org.jooq.Converter; import org.jooq.EnumType; import org.jooq.Field; import org.jooq.FieldProvider; @@ -628,6 +629,11 @@ class ResultImpl implements Result, AttachableInternal { return Convert.convert(getValues(field), type); } + @Override + public final List getValues(Field field, Converter converter) { + return Convert.convert(getValues(field), converter); + } + @Override public final List getValues(int fieldIndex) { return getValues(getField(fieldIndex)); @@ -638,6 +644,11 @@ class ResultImpl implements Result, AttachableInternal { return Convert.convert(getValues(fieldIndex), type); } + @Override + public final List getValues(int fieldIndex, Converter converter) { + return Convert.convert(getValues(fieldIndex), converter); + } + @Override public final List getValues(String fieldName) { return getValues(getField(fieldName)); @@ -648,6 +659,11 @@ class ResultImpl implements Result, AttachableInternal { return Convert.convert(getValues(fieldName), type); } + @Override + public final List getValues(String fieldName, Converter converter) { + return Convert.convert(getValues(fieldName), converter); + } + @Override public final List getValuesAsBigDecimal(Field field) { List result = new ArrayList(); diff --git a/jOOQ/src/main/java/org/jooq/tools/Convert.java b/jOOQ/src/main/java/org/jooq/tools/Convert.java index 459dda1820..901b1ae312 100644 --- a/jOOQ/src/main/java/org/jooq/tools/Convert.java +++ b/jOOQ/src/main/java/org/jooq/tools/Convert.java @@ -54,6 +54,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.jooq.Converter; import org.jooq.EnumType; import org.jooq.exception.DataTypeException; import org.jooq.tools.unsigned.UByte; @@ -63,6 +64,12 @@ import org.jooq.tools.unsigned.UShort; /** * Utility methods for type conversions + *

+ * This class provides less type-safety than the general jOOQ API methods. For + * instance, it accepts arbitrary {@link Converter} objects in methods like + * {@link #convert(List, Converter)} and {@link #convert(Object, Class)}, trying + * to retrofit the Object argument to the type provided in + * {@link Converter#fromType()} before performing the actual conversion. * * @author Lukas Eder */ @@ -103,6 +110,35 @@ public final class Convert { FALSE_VALUES = Collections.unmodifiableSet(falseValues); } + /** + * Convert an array into another one using a converter + *

+ * This uses {@link #convertArray(Object[], Class)} to convert the array to + * an array of {@link Converter#fromType()} first, before converting that + * array again to {@link Converter#toType()} + * + * @param from The array to convert + * @param converter The data type converter + * @return A converted array + * @throws DataTypeException - When the conversion is not possible + */ + @SuppressWarnings("unchecked") + public static U[] convertArray(Object[] from, Converter converter) throws DataTypeException { + if (from == null) { + return null; + } + else { + Object[] arrayOfT = convertArray(from, converter.fromType()); + Object[] arrayOfU = (Object[]) Array.newInstance(converter.toType(), from.length); + + for (int i = 0; i < arrayOfT.length; i++) { + arrayOfU[i] = convert(arrayOfT[i], converter); + } + + return (U[]) arrayOfU; + } + } + /** * Convert an array into another one by these rules *

@@ -151,6 +187,26 @@ public final class Convert { } } + /** + * Convert an object to a type. + * + * @param from The source object + * @param converter The data type converter + * @return The target type object + * @throws DataTypeException - When the conversion is not possible + */ + public static U convert(Object from, Converter converter) throws DataTypeException { + return convert0(from, converter); + } + + /** + * Conversion type-safety + */ + private static U convert0(Object from, Converter converter) throws DataTypeException { + ConvertAll all = new ConvertAll(converter.fromType()); + return converter.from(all.from(from)); + } + /** * Convert an object to a type. These are the conversion rules: *

    @@ -198,172 +254,8 @@ public final class Convert { * @return The converted object * @throws DataTypeException - When the conversion is not possible */ - @SuppressWarnings("unchecked") public static T convert(Object from, Class toClass) throws DataTypeException { - if (from == null) { - - // [#936] If types are converted to primitives, the result must not - // be null. Return the default value instead - if (toClass.isPrimitive()) { - - // Characters default to the "zero" character - if (toClass == char.class) { - return (T) Character.valueOf((char) 0); - } - - // All others can be converted from (int) 0 - else { - return convert(0, toClass); - } - } - else { - return null; - } - } - else { - final Class fromClass = from.getClass(); - - // No conversion - if (toClass == fromClass) { - return (T) from; - } - - // [#1155] Do this early: identity-conversion into Object - else if (toClass == Object.class) { - return (T) from; - } - else if (fromClass == byte[].class) { - - // This may not make much sense. Any other options? - return convert(Arrays.toString((byte[]) from), toClass); - } - else if (fromClass.isArray()) { - return (T) convertArray((Object[]) from, toClass); - } - - // All types can be converted into String - else if (toClass == String.class) { - if (from instanceof EnumType) { - return (T) ((EnumType) from).getLiteral(); - } - - return (T) from.toString(); - } - - // Various number types are converted between each other via String - else if (toClass == Byte.class || toClass == byte.class) { - return (T) Byte.valueOf(new BigDecimal(from.toString().trim()).byteValue()); - } - else if (toClass == Short.class || toClass == short.class) { - return (T) Short.valueOf(new BigDecimal(from.toString().trim()).shortValue()); - } - else if (toClass == Integer.class || toClass == int.class) { - return (T) Integer.valueOf(new BigDecimal(from.toString().trim()).intValue()); - } - else if (toClass == Long.class || toClass == long.class) { - if (java.util.Date.class.isAssignableFrom(fromClass)) { - return (T) Long.valueOf(((java.util.Date) from).getTime()); - } - else { - return (T) Long.valueOf(new BigDecimal(from.toString().trim()).longValue()); - } - } - - // ... this also includes unsigned number types - else if (toClass == UByte.class) { - return (T) ubyte(new BigDecimal(from.toString().trim()).shortValue()); - } - else if (toClass == UShort.class) { - return (T) ushort(new BigDecimal(from.toString().trim()).intValue()); - } - else if (toClass == UInteger.class) { - return (T) uint(new BigDecimal(from.toString().trim()).longValue()); - } - else if (toClass == ULong.class) { - if (java.util.Date.class.isAssignableFrom(fromClass)) { - return (T) ulong(((java.util.Date) from).getTime()); - } - else { - return (T) ulong(new BigDecimal(from.toString().trim()).toBigInteger().toString()); - } - } - - // ... and floating point / fixed point types - else if (toClass == Float.class || toClass == float.class) { - return (T) Float.valueOf(from.toString().trim()); - } - else if (toClass == Double.class || toClass == double.class) { - return (T) Double.valueOf(from.toString().trim()); - } - else if (toClass == BigDecimal.class) { - return (T) new BigDecimal(from.toString().trim()); - } - else if (toClass == BigInteger.class) { - return (T) new BigDecimal(from.toString().trim()).toBigInteger(); - } - else if (toClass == Boolean.class || toClass == boolean.class) { - String s = from.toString().toLowerCase().trim(); - - if (TRUE_VALUES.contains(s)) { - return (T) Boolean.TRUE; - } - else if (FALSE_VALUES.contains(s)) { - return (T) Boolean.FALSE; - } - else { - return null; - } - } - else if (toClass == Character.class || toClass == char.class) { - if (from.toString().length() != 1) { - throw fail(from, toClass); - } - - return (T) Character.valueOf(from.toString().charAt(0)); - } - - // Date types can be converted among each other - else if (java.util.Date.class.isAssignableFrom(fromClass)) { - return toDate(((java.util.Date) from).getTime(), toClass); - } - - // Long may also be converted into a date type - else if ((fromClass == Long.class || fromClass == long.class) && java.util.Date.class.isAssignableFrom(toClass)) { - return toDate((Long) from, toClass); - } - } - - throw fail(from, toClass); - } - - /** - * Convert a long timestamp to any date type - */ - @SuppressWarnings("unchecked") - private static T toDate(long time, Class toClass) { - if (toClass == Date.class) { - return (T) new Date(time); - } - else if (toClass == Time.class) { - return (T) new Time(time); - } - else if (toClass == Timestamp.class) { - return (T) new Timestamp(time); - } - else if (toClass == java.util.Date.class) { - return (T) new java.util.Date(time); - } - else if (toClass == Calendar.class) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(time); - return (T) calendar; - } - - throw fail(time, toClass); - } - - private static DataTypeException fail(Object from, Class toClass) { - return new DataTypeException("Cannot convert from " + from + " (" + from.getClass() + ") to " + toClass); + return convert(from, new ConvertAll(toClass)); } /** @@ -377,14 +269,235 @@ public final class Convert { * @see #convert(Object, Class) */ public static List convert(List list, Class type) throws DataTypeException { - List result = new ArrayList(); + return convert(list, new ConvertAll(type)); + } + + /** + * Convert a list of objects to a list of T, using + * {@link #convert(Object, Converter)} + * + * @param list The list of objects + * @param converter The data type converter + * @return The list of converted objects + * @throws DataTypeException - When the conversion is not possible + * @see #convert(Object, Converter) + */ + public static List convert(List list, Converter converter) throws DataTypeException { + return convert0(list, converter); + } + + /** + * Type safe conversion + */ + private static List convert0(List list, Converter converter) throws DataTypeException { + ConvertAll all = new ConvertAll(converter.fromType()); + List result = new ArrayList(); for (Object o : list) { - result.add(convert(o, type)); + result.add(convert(all.from(o), converter)); } return result; } + /** + * No instances + */ private Convert() {} + + /** + * The converter to convert them all. + */ + private static class ConvertAll implements Converter { + + private final Class toClass; + + ConvertAll(Class toClass) { + this.toClass = toClass; + } + + @SuppressWarnings("unchecked") + @Override + public U from(Object from) { + if (from == null) { + + // [#936] If types are converted to primitives, the result must not + // be null. Return the default value instead + if (toClass.isPrimitive()) { + + // Characters default to the "zero" character + if (toClass == char.class) { + return (U) Character.valueOf((char) 0); + } + + // All others can be converted from (int) 0 + else { + return convert(0, toClass); + } + } + else { + return null; + } + } + else { + final Class fromClass = from.getClass(); + + // No conversion + if (toClass == fromClass) { + return (U) from; + } + + // [#1155] Do this early: identity-conversion into Object + else if (toClass == Object.class) { + return (U) from; + } + else if (fromClass == byte[].class) { + + // This may not make much sense. Any other options? + return convert(Arrays.toString((byte[]) from), toClass); + } + else if (fromClass.isArray()) { + return (U) convertArray((Object[]) from, toClass); + } + + // All types can be converted into String + else if (toClass == String.class) { + if (from instanceof EnumType) { + return (U) ((EnumType) from).getLiteral(); + } + + return (U) from.toString(); + } + + // Various number types are converted between each other via String + else if (toClass == Byte.class || toClass == byte.class) { + return (U) Byte.valueOf(new BigDecimal(from.toString().trim()).byteValue()); + } + else if (toClass == Short.class || toClass == short.class) { + return (U) Short.valueOf(new BigDecimal(from.toString().trim()).shortValue()); + } + else if (toClass == Integer.class || toClass == int.class) { + return (U) Integer.valueOf(new BigDecimal(from.toString().trim()).intValue()); + } + else if (toClass == Long.class || toClass == long.class) { + if (java.util.Date.class.isAssignableFrom(fromClass)) { + return (U) Long.valueOf(((java.util.Date) from).getTime()); + } + else { + return (U) Long.valueOf(new BigDecimal(from.toString().trim()).longValue()); + } + } + + // ... this also includes unsigned number types + else if (toClass == UByte.class) { + return (U) ubyte(new BigDecimal(from.toString().trim()).shortValue()); + } + else if (toClass == UShort.class) { + return (U) ushort(new BigDecimal(from.toString().trim()).intValue()); + } + else if (toClass == UInteger.class) { + return (U) uint(new BigDecimal(from.toString().trim()).longValue()); + } + else if (toClass == ULong.class) { + if (java.util.Date.class.isAssignableFrom(fromClass)) { + return (U) ulong(((java.util.Date) from).getTime()); + } + else { + return (U) ulong(new BigDecimal(from.toString().trim()).toBigInteger().toString()); + } + } + + // ... and floating point / fixed point types + else if (toClass == Float.class || toClass == float.class) { + return (U) Float.valueOf(from.toString().trim()); + } + else if (toClass == Double.class || toClass == double.class) { + return (U) Double.valueOf(from.toString().trim()); + } + else if (toClass == BigDecimal.class) { + return (U) new BigDecimal(from.toString().trim()); + } + else if (toClass == BigInteger.class) { + return (U) new BigDecimal(from.toString().trim()).toBigInteger(); + } + else if (toClass == Boolean.class || toClass == boolean.class) { + String s = from.toString().toLowerCase().trim(); + + if (TRUE_VALUES.contains(s)) { + return (U) Boolean.TRUE; + } + else if (FALSE_VALUES.contains(s)) { + return (U) Boolean.FALSE; + } + else { + return null; + } + } + else if (toClass == Character.class || toClass == char.class) { + if (from.toString().length() != 1) { + throw fail(from, toClass); + } + + return (U) Character.valueOf(from.toString().charAt(0)); + } + + // Date types can be converted among each other + else if (java.util.Date.class.isAssignableFrom(fromClass)) { + return toDate(((java.util.Date) from).getTime(), toClass); + } + + // Long may also be converted into a date type + else if ((fromClass == Long.class || fromClass == long.class) && java.util.Date.class.isAssignableFrom(toClass)) { + return toDate((Long) from, toClass); + } + } + + throw fail(from, toClass); + } + + @Override + public Object to(U to) { + return to; + } + + @Override + public Class fromType() { + return Object.class; + } + + @Override + public Class toType() { + return (Class) toClass; + } + + /** + * Convert a long timestamp to any date type + */ + @SuppressWarnings("unchecked") + private static X toDate(long time, Class toClass) { + if (toClass == Date.class) { + return (X) new Date(time); + } + else if (toClass == Time.class) { + return (X) new Time(time); + } + else if (toClass == Timestamp.class) { + return (X) new Timestamp(time); + } + else if (toClass == java.util.Date.class) { + return (X) new java.util.Date(time); + } + else if (toClass == Calendar.class) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(time); + return (X) calendar; + } + + throw fail(time, toClass); + } + + private static DataTypeException fail(Object from, Class toClass) { + return new DataTypeException("Cannot convert from " + from + " (" + from.getClass() + ") to " + toClass); + } + } }