From 2e7ea84cd15c4416b41683a68812ed9e43ddca58 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Mon, 1 Aug 2011 09:42:11 +0000 Subject: [PATCH] [#198] Add SELECT INTO functionality [#782] Add T Record.getValue(..., Class) methods for convenient type conversion --- .../jooq/util/DefaultGeneratorStrategy.java | 21 +-- jOOQ-release/build.xml | 5 +- jOOQ-test/.classpath | 1 - jOOQ-test/lib/persistence-api-1.0.2.jar | Bin 53842 -> 0 bytes .../org/jooq/test/BookWithAnnotations.java | 107 ++++++++++++ .../org/jooq/test/BookWithoutAnnotations.java | 83 +++++++++ .../src/org/jooq/test/jOOQAbstractTest.java | 116 +++++++++++++ jOOQ/pom.xml | 8 + jOOQ/src/main/java/org/jooq/Record.java | 99 ++++++++++- jOOQ/src/main/java/org/jooq/Result.java | 13 ++ jOOQ/src/main/java/org/jooq/ResultQuery.java | 13 ++ jOOQ/src/main/java/org/jooq/Store.java | 25 +++ .../jooq/impl/AbstractDelegatingSelect.java | 5 + .../java/org/jooq/impl/AbstractRecord.java | 142 +++++++++++++-- .../org/jooq/impl/AbstractResultQuery.java | 5 + .../java/org/jooq/impl/AbstractStore.java | 11 ++ .../src/main/java/org/jooq/impl/JooqUtil.java | 164 +++++++++++++++++- .../main/java/org/jooq/impl/ResultImpl.java | 11 ++ .../main/java/org/jooq/impl/StringUtils.java | 44 +++++ 19 files changed, 826 insertions(+), 47 deletions(-) delete mode 100644 jOOQ-test/lib/persistence-api-1.0.2.jar create mode 100644 jOOQ-test/src/org/jooq/test/BookWithAnnotations.java create mode 100644 jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGeneratorStrategy.java b/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGeneratorStrategy.java index 407b887c3a..d8267b28e1 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGeneratorStrategy.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGeneratorStrategy.java @@ -209,26 +209,7 @@ public class DefaultGeneratorStrategy implements GeneratorStrategy { StringBuilder result = new StringBuilder(); String name = GenerationUtil.convertToJavaIdentifier(definition.getName()); - for (String word : name.split("_")) { - - // Uppercase first letter of a word - if (word.length() > 0) { - - // #82 - If a word starts with a digit, prevail the - // underscore to prevent naming clashes - if (Character.isDigit(word.charAt(0))) { - result.append("_"); - } - - result.append(word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase()); - } - - // If no letter exists, prevail the underscore (e.g. leading - // underscores) - else { - result.append("_"); - } - } + result.append(StringUtils.toCamelCase(name)); if (!StringUtils.isEmpty(suffix)) { result.append(suffix); diff --git a/jOOQ-release/build.xml b/jOOQ-release/build.xml index 12375ca097..5e3e0d14ed 100644 --- a/jOOQ-release/build.xml +++ b/jOOQ-release/build.xml @@ -40,7 +40,10 @@ - + + + + diff --git a/jOOQ-test/.classpath b/jOOQ-test/.classpath index 67764557d0..fa9b95d23f 100644 --- a/jOOQ-test/.classpath +++ b/jOOQ-test/.classpath @@ -24,6 +24,5 @@ - diff --git a/jOOQ-test/lib/persistence-api-1.0.2.jar b/jOOQ-test/lib/persistence-api-1.0.2.jar deleted file mode 100644 index a18dd1bcd26fbb425a796da48ee81850f0e93092..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53842 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 NlmMf=50&tL{RayslhptK diff --git a/jOOQ-test/src/org/jooq/test/BookWithAnnotations.java b/jOOQ-test/src/org/jooq/test/BookWithAnnotations.java new file mode 100644 index 0000000000..4ab25f0efc --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/BookWithAnnotations.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2009-2011, 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 javax.persistence.Column; + +/** + * @author Lukas Eder + */ +public class BookWithAnnotations { + + // JPA-annotated members + // --------------------- + @Column(name = "ID") + public Integer id; + + @Column(name = "ID") + public int id2; + + @Column(name = "TITLE") + public String title; + + @Column(name = "FIRST_NAME") + public String firstName; + + // Members without annotations + // --------------------------- + public int id3; + public String firstName2; + public String lastName; + public String lastName2; + + // Methods with annotations + // ------------------------ + @Column(name = "ID") + public void setId(int id) { + id3 = id; + } + + @Column(name = "FIRST_NAME") + public void setFirstName(String f) { + firstName2 = f; + } + + @Column(name = "LAST_NAME") + public void setLastName(String l) { + lastName = l; + } + + public void setLAST_NAME(String l) { + lastName2 = l; + } + + @Column(name = "LAST_NAME") + public String getLAST_NAME() { + return lastName2; + } + + @Column(name = "LAST_NAME") + @SuppressWarnings("unused") + public void tooManyParameters(String l, String tooMany) { + throw new AssertionError(); + } + + @Column(name = "LAST_NAME") + public void notEnoughParameters() { + throw new AssertionError(); + } + + @Override + public String toString() { + return "JPABook [id=" + id + ", title=" + title + ", firstName=" + firstName + ", lastName=" + lastName + "]"; + } +} diff --git a/jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java b/jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java new file mode 100644 index 0000000000..45f98d5802 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2009-2011, 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; + + +/** + * @author Lukas Eder + */ +public class BookWithoutAnnotations { + + public Integer id; + public int id2; + public int ID; + public String title; + public String firstName; + public String firstName2; + public String lastName; + public String lastName2; + public String LAST_NAME; + + public void setId(int id) { + id2 = id; + } + + public void setFirstName(String f) { + firstName2 = f; + } + + public void setLAST_NAME(String l) { + lastName = l; + } + + public void LAST_NAME(String l) { + lastName2 = l; + } + + @SuppressWarnings("unused") + public void setLAST_NAME(String l, String tooManyParameters) { + throw new AssertionError(); + } + + public void setLAST_NAME() { + throw new AssertionError(); + } + + @Override + public String toString() { + return "JPABook [id=" + id + ", title=" + title + ", firstName=" + firstName + ", lastName=" + lastName + "]"; + } +} diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index b01540d71c..5018ebee27 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -2133,6 +2133,122 @@ public abstract class jOOQAbstractTest< assertEquals("Coelho", record.getValue(TAuthor_LAST_NAME())); } + @Test + public void testFetchIntoWithAnnotations() throws Exception { + List result = + create().select( + TBook_ID(), + TBook_TITLE(), + TAuthor_FIRST_NAME(), + TAuthor_LAST_NAME()) + .from(TBook()) + .join(TAuthor()).on(TBook_AUTHOR_ID().equal(TAuthor_ID())) + .orderBy(TBook_ID()) + .fetchInto(BookWithAnnotations.class); + + assertEquals(4, result.size()); + + assertEquals(1, (int) result.get(0).id); + assertEquals(2, (int) result.get(1).id); + assertEquals(3, (int) result.get(2).id); + assertEquals(4, (int) result.get(3).id); + + assertEquals(1, result.get(0).id2); + assertEquals(2, result.get(1).id2); + assertEquals(3, result.get(2).id2); + assertEquals(4, result.get(3).id2); + + assertEquals(1, result.get(0).id3); + assertEquals(2, result.get(1).id3); + assertEquals(3, result.get(2).id3); + assertEquals(4, result.get(3).id3); + + assertEquals("1984", result.get(0).title); + assertEquals("Animal Farm", result.get(1).title); + assertEquals("O Alquimista", result.get(2).title); + assertEquals("Brida", result.get(3).title); + + assertEquals("George", result.get(0).firstName); + assertEquals("George", result.get(1).firstName); + assertEquals("Paulo", result.get(2).firstName); + assertEquals("Paulo", result.get(3).firstName); + + assertEquals("George", result.get(0).firstName2); + assertEquals("George", result.get(1).firstName2); + assertEquals("Paulo", result.get(2).firstName2); + assertEquals("Paulo", result.get(3).firstName2); + + assertEquals("Orwell", result.get(0).lastName); + assertEquals("Orwell", result.get(1).lastName); + assertEquals("Coelho", result.get(2).lastName); + assertEquals("Coelho", result.get(3).lastName); + + assertEquals("Orwell", result.get(0).lastName2); + assertEquals("Orwell", result.get(1).lastName2); + assertEquals("Coelho", result.get(2).lastName2); + assertEquals("Coelho", result.get(3).lastName2); + } + + @Test + public void testFetchIntoWithoutAnnotations() throws Exception { + List result = + create().select( + TBook_ID(), + TBook_TITLE(), + TAuthor_FIRST_NAME(), + TAuthor_LAST_NAME()) + .from(TBook()) + .join(TAuthor()).on(TBook_AUTHOR_ID().equal(TAuthor_ID())) + .orderBy(TBook_ID()) + .fetchInto(BookWithoutAnnotations.class); + + assertEquals(4, result.size()); + + assertEquals(1, (int) result.get(0).id); + assertEquals(2, (int) result.get(1).id); + assertEquals(3, (int) result.get(2).id); + assertEquals(4, (int) result.get(3).id); + + assertEquals(1, result.get(0).id2); + assertEquals(2, result.get(1).id2); + assertEquals(3, result.get(2).id2); + assertEquals(4, result.get(3).id2); + + assertEquals(1, result.get(0).ID); + assertEquals(2, result.get(1).ID); + assertEquals(3, result.get(2).ID); + assertEquals(4, result.get(3).ID); + + assertEquals("1984", result.get(0).title); + assertEquals("Animal Farm", result.get(1).title); + assertEquals("O Alquimista", result.get(2).title); + assertEquals("Brida", result.get(3).title); + + assertEquals("George", result.get(0).firstName); + assertEquals("George", result.get(1).firstName); + assertEquals("Paulo", result.get(2).firstName); + assertEquals("Paulo", result.get(3).firstName); + + assertEquals("George", result.get(0).firstName2); + assertEquals("George", result.get(1).firstName2); + assertEquals("Paulo", result.get(2).firstName2); + assertEquals("Paulo", result.get(3).firstName2); + + assertEquals("Orwell", result.get(0).lastName); + assertEquals("Orwell", result.get(1).lastName); + assertEquals("Coelho", result.get(2).lastName); + assertEquals("Coelho", result.get(3).lastName); + + assertEquals("Orwell", result.get(0).lastName2); + assertEquals("Orwell", result.get(1).lastName2); + assertEquals("Coelho", result.get(2).lastName2); + assertEquals("Coelho", result.get(3).lastName2); + + assertEquals("Orwell", result.get(0).LAST_NAME); + assertEquals("Orwell", result.get(1).LAST_NAME); + assertEquals("Coelho", result.get(2).LAST_NAME); + assertEquals("Coelho", result.get(3).LAST_NAME); + } @Test public void testGrouping() throws Exception { diff --git a/jOOQ/pom.xml b/jOOQ/pom.xml index f03d9a52e8..f613c78ccd 100644 --- a/jOOQ/pom.xml +++ b/jOOQ/pom.xml @@ -180,5 +180,13 @@ jar test + + javax.persistence + persistence-api + 1.0 + jar + provided + true + \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/Record.java b/jOOQ/src/main/java/org/jooq/Record.java index 72cb4c69dd..83b82ac797 100644 --- a/jOOQ/src/main/java/org/jooq/Record.java +++ b/jOOQ/src/main/java/org/jooq/Record.java @@ -42,6 +42,8 @@ import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import javax.persistence.Column; + /** * A wrapper for database result records returned by * {@link SelectQuery} @@ -792,6 +794,58 @@ public interface Record extends FieldProvider, Store { */ Time getValueAsTime(String fieldName, Time defaultValue) throws IllegalArgumentException; + /** + * Get a converted value from this Record, providing a field. + * + * @param The conversion type parameter + * @param field The field + * @param type The conversion type + * @return The value of a field contained in this record + * @throws IllegalArgumentException If the argument field is not contained + * in {@link #getFields()} + */ + T getValue(Field field, Class type) throws IllegalArgumentException; + + /** + * Get a converted value from this record, providing a field. + * + * @param The conversion type parameter + * @param field The field + * @param type The conversion type + * @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()} + */ + T getValue(Field field, Class type, T defaultValue) throws IllegalArgumentException; + + /** + * Get a converted value from this Record, providing a field name. + * + * @param The conversion type parameter + * @param fieldName The field's name + * @param type The conversion type + * @return The value of a field's name contained in this record + * @throws IllegalArgumentException If the argument fieldName is not + * contained in the record + */ + T getValue(String fieldName, Class type) throws IllegalArgumentException; + + /** + * Get a converted value from this record, providing a field name. + * + * @param The conversion type parameter + * @param fieldName The field's name + * @param type The conversion type + * @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 + */ + T getValue(String fieldName, Class type, T defaultValue) throws IllegalArgumentException; + /** * Set a value into this record. * @@ -802,11 +856,46 @@ public interface Record extends FieldProvider, Store { void setValue(Field field, T value); /** - * Whether any values have been changed since the record was loaded. + * Map resulting records onto a custom type. The mapping algorithm is this: + *

If any JPA {@link Column} annotations are found on the provided + * type, only those are used:

+ *
    + *
  • If type contains public single-argument methods + * annotated with Column, those methods are invoked
  • + *
  • If type contains public no-argument methods starting + * with getXXX or isXXX, annotated with + * Column, then matching setXXX() methods are + * invoked
  • + *
  • If type contains public members annotated with + * Column, those members are set
  • + *
  • The same annotation can be re-used for several methods/members
  • + *
  • {@link Column#name()} must match {@link Field#getName()}. All other + * annotation attributes are ignored
  • + *
+ *

If there are no JPA Column annotations, or jOOQ can't + * find the javax.persistence API on the classpath, jOOQ will + * map Record values by naming convention:

If a field's + * value for {@link Field#getName()} is MY_field + * (case-sensitive!), then this field's value will be set on all of these: + *
    + *
  • Public member MY_field
  • + *
  • Public member myField
  • + *
  • Public method MY_field(...)
  • + *
  • Public method myField(...)
  • + *
  • Public method setMY_field(...)
  • + *
  • Public method setMyField(...)
  • + *
+ *

Other restrictions

+ *
    + *
  • type must provide a public default constructor
  • + *
  • primitive types are supported. If a value is null, this + * will result in setting the primitive type's default value (zero for + * numbers, or false for booleans). Hence, there is no way of + * distinguishing null and 0 in that case.
  • + *
* - * @deprecated - This method is part of the internal API and will be removed - * in the future. + * @param The generic entity type. + * @param type The entity type. */ - @Deprecated - boolean hasChangedValues(); + E into(Class type); } diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index d102044160..fc6b24f08b 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -1705,4 +1705,17 @@ public interface Result extends FieldProvider, List, Attach * href="http://www.jooq.org/xsd/jooq-export-1.6.2.xsd">http://www.jooq.org/xsd/jooq-export-1.6.2.xsd */ String formatXML(); + + /** + * Map resulting records onto a custom type. + *

+ * This is the same as calling record.into(type) on every + * record contained in this Result. See + * {@link Record#into(Class)} for more details + * + * @param The generic entity type. + * @param type The entity type. + * @see Record#into(Class) + */ + List into(Class type); } diff --git a/jOOQ/src/main/java/org/jooq/ResultQuery.java b/jOOQ/src/main/java/org/jooq/ResultQuery.java index 940123b249..8d4ad03e05 100644 --- a/jOOQ/src/main/java/org/jooq/ResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/ResultQuery.java @@ -325,6 +325,19 @@ public interface ResultQuery extends Query { */ Object[] fetchOneArray() throws SQLException; + /** + * Map resulting records onto a custom type. + *

+ * This is the same as calling fetch().into(type). See + * {@link Record#into(Class)} for more details + * + * @param The generic entity type. + * @param type The entity type. + * @see Record#into(Class) + * @see Result#into(Class) + */ + List fetchInto(Class type) throws SQLException; + /** * The record type produced by this query */ diff --git a/jOOQ/src/main/java/org/jooq/Store.java b/jOOQ/src/main/java/org/jooq/Store.java index f3b53baefa..f798b1434d 100644 --- a/jOOQ/src/main/java/org/jooq/Store.java +++ b/jOOQ/src/main/java/org/jooq/Store.java @@ -421,4 +421,29 @@ public interface Store extends Attachable { */ Timestamp getValueAsTimestamp(int index, Timestamp defaultValue) throws IllegalArgumentException; + /** + * Get a converted value from this Store, providing a field index. + * + * @param The conversion type parameter + * @param index The field's index + * @param type The conversion type + * @return The value of a field's index contained in this Store + * @throws IllegalArgumentException If the argument index is not contained + * in the Store + */ + T getValue(int index, Class type) throws IllegalArgumentException; + + /** + * Get a converted value from this Store, providing a field index. + * + * @param The conversion type parameter + * @param index The field's index + * @param type The conversion type + * @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 + */ + T getValue(int index, Class type, T defaultValue) throws IllegalArgumentException; } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java index 98319c632d..ac72dd9254 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java @@ -188,6 +188,11 @@ abstract class AbstractDelegatingSelect extends AbstractQueryP return query.fetchOneArray(); } + @Override + public final List fetchInto(Class type) throws SQLException { + return query.fetchInto(type); + } + @Override public final int execute() throws SQLException { return query.execute(); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index 0a27926222..50194c377b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -36,6 +36,8 @@ package org.jooq.impl; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Date; @@ -75,7 +77,7 @@ abstract class AbstractRecord extends AbstractStore implements Record { this.fields = fields; } - FieldProvider getMetaData() { + final FieldProvider getMetaData() { return fields; } @@ -137,7 +139,7 @@ abstract class AbstractRecord extends AbstractStore implements Record { return fields.getIndex(field); } - private void init() { + private final void init() { values = new Value[fields.getFields().size()]; for (int i = 0; i < values.length; i++) { @@ -164,17 +166,6 @@ abstract class AbstractRecord extends AbstractStore implements Record { getValues()[getIndex(field)] = value; } - @Override - public final boolean hasChangedValues() { - for (Value value : getValues()) { - if (value.isChanged()) { - return true; - } - } - - return false; - } - @Override public final String toString() { return getClass().getSimpleName() + " [values=" + Arrays.asList(getValues()) + "]"; @@ -473,4 +464,129 @@ abstract class AbstractRecord extends AbstractStore implements Record { public final Time getValueAsTime(String fieldName, Time defaultValue) throws IllegalArgumentException { return getValueAsTime(getField(fieldName), defaultValue); } + + @Override + public final T getValue(Field field, Class type) throws IllegalArgumentException { + return TypeUtils.convert(getValue(field), type); + } + + @Override + public final T getValue(Field field, Class type, T defaultValue) throws IllegalArgumentException { + final T result = getValue(field, type); + return result == null ? defaultValue : result; + } + + @Override + public final T getValue(String fieldName, Class type) throws IllegalArgumentException { + return TypeUtils.convert(getValue(fieldName), type); + } + + @Override + public final Z getValue(String fieldName, Class type, Z defaultValue) throws IllegalArgumentException { + final Z result = getValue(fieldName, type); + return result == null ? defaultValue : result; + } + + @Override + public final T into(Class type) { + try { + T result = type.newInstance(); + + for (Field field : getFields()) { + List members; + List methods; + + // Annotations are available and present + if (JooqUtil.isJPAAvailable() && JooqUtil.hasColumnAnnotations(type)) { + members = JooqUtil.getAnnotatedMembers(type, field.getName()); + methods = JooqUtil.getAnnotatedMethods(type, field.getName()); + } + + // No annotations are present + else { + members = JooqUtil.getMatchingMembers(type, field.getName()); + methods = JooqUtil.getMatchingMethods(type, field.getName()); + } + + for (java.lang.reflect.Field member : members) { + copyInto(result, member, field); + } + + for (java.lang.reflect.Method method : methods) { + copyInto(result, method, field); + } + } + + return result; + } + + // All reflection exceptions are intercepted + catch (Exception e) { + throw new IllegalStateException("An error ocurred when mapping record to " + type, e); + } + } + + private final void copyInto(Object result, Method method, Field field) + throws IllegalAccessException, InvocationTargetException { + + Class mType = method.getParameterTypes()[0]; + + if (mType.isPrimitive()) { + if (mType == byte.class) { + method.invoke(result, getValueAsByte(field, (byte) 0)); + } + else if (mType == short.class) { + method.invoke(result, getValueAsShort(field, (short) 0)); + } + else if (mType == int.class) { + method.invoke(result, getValueAsInteger(field, 0)); + } + else if (mType == long.class) { + method.invoke(result, getValueAsLong(field, 0L)); + } + else if (mType == float.class) { + method.invoke(result, getValueAsFloat(field, 0.0f)); + } + else if (mType == double.class) { + method.invoke(result, getValueAsDouble(field, 0.0)); + } + else if (mType == boolean.class) { + method.invoke(result, getValueAsBoolean(field, false)); + } + } + else { + method.invoke(result, getValue(field, mType)); + } + } + + private final void copyInto(Object result, java.lang.reflect.Field member, Field field) throws IllegalAccessException { + Class mType = member.getType(); + + if (mType.isPrimitive()) { + if (mType == byte.class) { + member.setByte(result, getValueAsByte(field, (byte) 0)); + } + else if (mType == short.class) { + member.setShort(result, getValueAsShort(field, (short) 0)); + } + else if (mType == int.class) { + member.setInt(result, getValueAsInteger(field, 0)); + } + else if (mType == long.class) { + member.setLong(result, getValueAsLong(field, 0L)); + } + else if (mType == float.class) { + member.setFloat(result, getValueAsFloat(field, 0.0f)); + } + else if (mType == double.class) { + member.setDouble(result, getValueAsDouble(field, 0.0)); + } + else if (mType == boolean.class) { + member.setBoolean(result, getValueAsBoolean(field, false)); + } + } + else { + member.set(result, getValue(field, mType)); + } + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index cab11aa36f..daa39c981a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -282,6 +282,11 @@ abstract class AbstractResultQuery extends AbstractQuery imple return convertToArray(fetchOne()); } + @Override + public final List fetchInto(Class type) throws SQLException { + return fetch().into(type); + } + private final Object[] convertToArray(R record) { final List> fields = record.getFields(); Object[] array = new Object[fields.size()]; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java index 75648ec348..f09d487dea 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java @@ -244,6 +244,17 @@ abstract class AbstractStore implements Store, AttachableInternal { return result == null ? defaultValue : result; } + @Override + public final Z getValue(int index, Class type) throws IllegalArgumentException { + return TypeUtils.convert(getValue(index), type); + } + + @Override + public final Z getValue(int index, Class type, Z defaultValue) throws IllegalArgumentException { + final Z result = getValue(index, type); + return result == null ? defaultValue : result; + } + // ------------------------------------------------------------------------- // equals and hashCode // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/JooqUtil.java b/jOOQ/src/main/java/org/jooq/impl/JooqUtil.java index ab95bf2418..c5cce0a98a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JooqUtil.java +++ b/jOOQ/src/main/java/org/jooq/impl/JooqUtil.java @@ -35,9 +35,14 @@ */ package org.jooq.impl; +import java.lang.reflect.Method; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.Column; import org.jooq.ArrayRecord; import org.jooq.Configuration; @@ -54,18 +59,25 @@ import org.jooq.RenderContext; */ final class JooqUtil { + /** + * Indicating whether JPA (javax.persistence) is on the + * classpath. + */ + private static Boolean isJPAAvailable; + /** * Create a new Oracle-style VARRAY {@link ArrayRecord} */ static > R newArrayRecord(Class type, Configuration configuration) { - try{ - return type.getConstructor(Configuration.class).newInstance(configuration); - } catch (Exception e) { - throw new IllegalStateException( - "ArrayRecord type does not provide a constructor with signature ArrayRecord(FieldProvider) : " + type + - ". Exception : " + e.getMessage()); + try { + return type.getConstructor(Configuration.class).newInstance(configuration); + } + catch (Exception e) { + throw new IllegalStateException( + "ArrayRecord type does not provide a constructor with signature ArrayRecord(FieldProvider) : " + type + + ". Exception : " + e.getMessage()); - } + } } /** @@ -228,4 +240,142 @@ final class JooqUtil { safeClose(resultSet); safeClose(statement); } + + /** + * Check if JPA classes can be loaded. This is only done once per JVM! + */ + static boolean isJPAAvailable() { + if (isJPAAvailable == null) { + try { + Class.forName(Column.class.getName()); + isJPAAvailable = true; + } + catch (Exception e) { + isJPAAvailable = false; + } + } + + return isJPAAvailable; + } + + /** + * Check whether type has any {@link Column} annotated members + * or methods + */ + static final boolean hasColumnAnnotations(Class type) { + for (java.lang.reflect.Field member : type.getFields()) { + if (member.getAnnotation(Column.class) != null) { + return true; + } + } + + for (Method method : type.getMethods()) { + if (method.getAnnotation(Column.class) != null) { + return true; + } + } + + return false; + } + + /** + * Get all members annotated with a given column name + */ + static final List getAnnotatedMembers(Class type, String name) { + List result = new ArrayList(); + + for (java.lang.reflect.Field member : type.getFields()) { + Column annotation = member.getAnnotation(Column.class); + + if (annotation != null) { + if (name.equals(annotation.name())) { + result.add(member); + } + } + } + + return result; + } + + /** + * Get all members matching a given column name + */ + static final List getMatchingMembers(Class type, String name) { + List result = new ArrayList(); + + for (java.lang.reflect.Field member : type.getFields()) { + if (name.equals(member.getName())) { + result.add(member); + } + else if (StringUtils.toCamelCaseLC(name).equals(member.getName())) { + result.add(member); + } + } + + return result; + } + + /** + * Get all methods annotated with a given column name + */ + static final List getAnnotatedMethods(Class type, String name) { + List result = new ArrayList(); + + for (Method method : type.getMethods()) { + Column annotation = method.getAnnotation(Column.class); + + if (annotation != null && name.equals(annotation.name())) { + + // Annotated setter + if (method.getParameterTypes().length == 1) { + result.add(method); + } + + // Annotated getter with matching setter + else if (method.getParameterTypes().length == 0) { + String m = method.getName(); + + if (m.startsWith("get") || m.startsWith("is")) { + try { + Method setter = type.getMethod("set" + m.substring(3), method.getReturnType()); + + // Setter annotation is more relevant + if (setter.getAnnotation(Column.class) == null) { + result.add(setter); + } + } + catch (NoSuchMethodException ignore) {} + } + } + } + } + + return result; + } + + /** + * Get all methods matching a given column name + */ + static final List getMatchingMethods(Class type, String name) { + List result = new ArrayList(); + + for (Method method : type.getMethods()) { + if (method.getParameterTypes().length == 1) { + if (name.equals(method.getName())) { + result.add(method); + } + else if (StringUtils.toCamelCaseLC(name).equals(method.getName())) { + result.add(method); + } + else if (("set" + name).equals(method.getName())) { + result.add(method); + } + else if (("set" + StringUtils.toCamelCase(name)).equals(method.getName())) { + result.add(method); + } + } + } + + return result; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java index 4decd1d8cb..3b05f4c948 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ResultImpl.java @@ -1171,6 +1171,17 @@ class ResultImpl implements Result, AttachableInternal { new String[] { """, "'", "<", ">", "&"}); } + @Override + public final List into(Class type) { + List list = new ArrayList(); + + for (Record record : this) { + list.add(record.into(type)); + } + + return list; + } + // ------------------------------------------------------------------------- // Object API // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/StringUtils.java b/jOOQ/src/main/java/org/jooq/impl/StringUtils.java index 0e57a41805..6d4324bc6d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/StringUtils.java +++ b/jOOQ/src/main/java/org/jooq/impl/StringUtils.java @@ -851,4 +851,48 @@ public class StringUtils { } private StringUtils() {} + + // ------------------------------------------------------------------------- + // Custom additions to StringUtils. The following methods are not part of + // Apache's commons-lang library + // ------------------------------------------------------------------------- + + /** + * Convert a string to camel case + */ + public static String toCamelCase(String string) { + StringBuilder result = new StringBuilder(); + + for (String word : string.split("_")) { + + // Uppercase first letter of a word + if (word.length() > 0) { + + // [#82] - If a word starts with a digit, prevail the + // underscore to prevent naming clashes + if (Character.isDigit(word.charAt(0))) { + result.append("_"); + } + + result.append(word.substring(0, 1).toUpperCase()); + result.append(word.substring(1).toLowerCase()); + } + + // If no letter exists, prevail the underscore (e.g. leading + // underscores) + else { + result.append("_"); + } + } + + return result.toString(); + } + + /** + * Convert a string to camel case starting with a lower case letter + */ + public static String toCamelCaseLC(String string) { + String cc = toCamelCase(string); + return cc.substring(0, 1).toLowerCase() + cc.substring(1); + } }