[#198] Add SELECT INTO functionality

[#782] Add <T> T Record.getValue(..., Class<? extends T>) methods for convenient type conversion
This commit is contained in:
Lukas Eder 2011-08-01 09:42:11 +00:00
parent d190cc7b60
commit 2e7ea84cd1
19 changed files with 826 additions and 47 deletions

View File

@ -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);

View File

@ -40,7 +40,10 @@
<copy todir="${dir.release}/${version}/third-party/lib" file="C:\Users\lukas\.m2\repository\log4j\log4j\1.2.16\log4j-1.2.16.jar"/>
<copy todir="${dir.release}/${version}/third-party/src" file="C:\Users\lukas\.m2\repository\log4j\log4j\1.2.16\log4j-1.2.16-sources.jar"/>
<copy todir="${dir.release}/${version}/third-party/lib" file="C:\Users\lukas\.m2\repository\org\apache\ant\ant\1.8.2\ant-1.8.2.jar"/>
<copy todir="${dir.release}/${version}/third-party/lib" file="C:\Users\lukas\.m2\repository\javax\persistence\persistence-api\1.0\persistence-api-1.0.jar"/>
<copy todir="${dir.release}/${version}/third-party/src" file="C:\Users\lukas\.m2\repository\javax\persistence\persistence-api\1.0\persistence-api-1.0-sources.jar"/>
<copy todir="${dir.release}/${version}/third-party/lib" file="C:\Users\lukas\.m2\repository\org\apache\ant\ant\1.8.2\ant-1.8.2.jar"/>
<zip destfile="${dir.release}/versions/jOOQ-${version}-full.zip">
<fileset dir="${dir.release}/${version}"/>

View File

@ -24,6 +24,5 @@
<classpathentry exported="true" kind="lib" path="lib/log4j-1.2.16.jar"/>
<classpathentry exported="true" kind="lib" path="lib/jconn3.jar"/>
<classpathentry exported="true" kind="lib" path="lib/iijdbc.jar"/>
<classpathentry kind="lib" path="lib/persistence-api-1.0.2.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -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 + "]";
}
}

View File

@ -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 + "]";
}
}

View File

@ -2133,6 +2133,122 @@ public abstract class jOOQAbstractTest<
assertEquals("Coelho", record.getValue(TAuthor_LAST_NAME()));
}
@Test
public void testFetchIntoWithAnnotations() throws Exception {
List<BookWithAnnotations> 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<BookWithoutAnnotations> 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 {

View File

@ -180,5 +180,13 @@
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
<type>jar</type>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -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
* <code>{@link SelectQuery}</code>
@ -792,6 +794,58 @@ public interface Record extends FieldProvider, Store<Object> {
*/
Time getValueAsTime(String fieldName, Time defaultValue) throws IllegalArgumentException;
/**
* Get a converted value from this Record, providing a field.
*
* @param <T> 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> T getValue(Field<?> field, Class<? extends T> type) throws IllegalArgumentException;
/**
* Get a converted value from this record, providing a field.
*
* @param <T> The conversion type parameter
* @param field The field
* @param type The conversion type
* @param defaultValue The default value instead of <code>null</code>
* @return The value of a field contained in this record, or defaultValue,
* if <code>null</code>
* @throws IllegalArgumentException If the argument field is not contained
* in {@link #getFields()}
*/
<T> T getValue(Field<?> field, Class<? extends T> type, T defaultValue) throws IllegalArgumentException;
/**
* Get a converted value from this Record, providing a field name.
*
* @param <T> 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> T getValue(String fieldName, Class<? extends T> type) throws IllegalArgumentException;
/**
* Get a converted value from this record, providing a field name.
*
* @param <T> The conversion type parameter
* @param fieldName The field's name
* @param type The conversion type
* @param defaultValue The default value instead of <code>null</code>
* @return The value of a field's name contained in this record, or
* defaultValue, if <code>null</code>
* @throws IllegalArgumentException If the argument fieldName is not
* contained in the record
*/
<T> T getValue(String fieldName, Class<? extends T> type, T defaultValue) throws IllegalArgumentException;
/**
* Set a value into this record.
*
@ -802,11 +856,46 @@ public interface Record extends FieldProvider, Store<Object> {
<T> void setValue(Field<T> 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:
* <h3>If any JPA {@link Column} annotations are found on the provided
* <code>type</code>, only those are used:</h3>
* <ul>
* <li>If <code>type</code> contains public single-argument methods
* annotated with <code>Column</code>, those methods are invoked</li>
* <li>If <code>type</code> contains public no-argument methods starting
* with <code>getXXX</code> or <code>isXXX</code>, annotated with
* <code>Column</code>, then matching <code>setXXX()</code> methods are
* invoked</li>
* <li>If <code>type</code> contains public members annotated with
* <code>Column</code>, those members are set</li>
* <li>The same annotation can be re-used for several methods/members</li>
* <li>{@link Column#name()} must match {@link Field#getName()}. All other
* annotation attributes are ignored</li>
* </ul>
* <h3>If there are no JPA <code>Column</code> annotations, or jOOQ can't
* find the <code>javax.persistence</code> API on the classpath, jOOQ will
* map <code>Record</code> values by naming convention:</h3> If a field's
* value for {@link Field#getName()} is <code>MY_field</code>
* (case-sensitive!), then this field's value will be set on all of these:
* <ul>
* <li>Public member <code>MY_field</code></li>
* <li>Public member <code>myField</code></li>
* <li>Public method <code>MY_field(...)</code></li>
* <li>Public method <code>myField(...)</code></li>
* <li>Public method <code>setMY_field(...)</code></li>
* <li>Public method <code>setMyField(...)</code></li>
* </ul>
* <h3>Other restrictions</h3>
* <ul>
* <li><code>type</code> must provide a public default constructor</li>
* <li>primitive types are supported. If a value is <code>null</code>, this
* will result in setting the primitive type's default value (zero for
* numbers, or <code>false</code> for booleans). Hence, there is no way of
* distinguishing <code>null</code> and <code>0</code> in that case.</li>
* </ul>
*
* @deprecated - This method is part of the internal API and will be removed
* in the future.
* @param <E> The generic entity type.
* @param type The entity type.
*/
@Deprecated
boolean hasChangedValues();
<E> E into(Class<? extends E> type);
}

View File

@ -1705,4 +1705,17 @@ public interface Result<R extends Record> extends FieldProvider, List<R>, Attach
* href="http://www.jooq.org/xsd/jooq-export-1.6.2.xsd">http://www.jooq.org/xsd/jooq-export-1.6.2.xsd</a>
*/
String formatXML();
/**
* Map resulting records onto a custom type.
* <p>
* This is the same as calling <code>record.into(type)</code> on every
* record contained in this <code>Result</code>. See
* {@link Record#into(Class)} for more details
*
* @param <E> The generic entity type.
* @param type The entity type.
* @see Record#into(Class)
*/
<E> List<E> into(Class<? extends E> type);
}

View File

@ -325,6 +325,19 @@ public interface ResultQuery<R extends Record> extends Query {
*/
Object[] fetchOneArray() throws SQLException;
/**
* Map resulting records onto a custom type.
* <p>
* This is the same as calling <code>fetch().into(type)</code>. See
* {@link Record#into(Class)} for more details
*
* @param <E> The generic entity type.
* @param type The entity type.
* @see Record#into(Class)
* @see Result#into(Class)
*/
<E> List<E> fetchInto(Class<? extends E> type) throws SQLException;
/**
* The record type produced by this query
*/

View File

@ -421,4 +421,29 @@ public interface Store<E> extends Attachable {
*/
Timestamp getValueAsTimestamp(int index, Timestamp defaultValue) throws IllegalArgumentException;
/**
* Get a converted value from this Store, providing a field index.
*
* @param <T> 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> T getValue(int index, Class<? extends T> type) throws IllegalArgumentException;
/**
* Get a converted value from this Store, providing a field index.
*
* @param <T> The conversion type parameter
* @param index The field's index
* @param type The conversion type
* @param defaultValue The default value instead of <code>null</code>
* @return The value of a field's index contained in this Store, or
* defaultValue, if <code>null</code>
* @throws IllegalArgumentException If the argument index is not contained
* in the Store
*/
<T> T getValue(int index, Class<? extends T> type, T defaultValue) throws IllegalArgumentException;
}

View File

@ -188,6 +188,11 @@ abstract class AbstractDelegatingSelect<R extends Record> extends AbstractQueryP
return query.fetchOneArray();
}
@Override
public final <T> List<T> fetchInto(Class<? extends T> type) throws SQLException {
return query.fetchInto(type);
}
@Override
public final int execute() throws SQLException {
return query.execute();

View File

@ -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<Object> implements Record {
this.fields = fields;
}
FieldProvider getMetaData() {
final FieldProvider getMetaData() {
return fields;
}
@ -137,7 +139,7 @@ abstract class AbstractRecord extends AbstractStore<Object> 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<Object> 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<Object> implements Record {
public final Time getValueAsTime(String fieldName, Time defaultValue) throws IllegalArgumentException {
return getValueAsTime(getField(fieldName), defaultValue);
}
@Override
public final <T> T getValue(Field<?> field, Class<? extends T> type) throws IllegalArgumentException {
return TypeUtils.convert(getValue(field), type);
}
@Override
public final <T> T getValue(Field<?> field, Class<? extends T> type, T defaultValue) throws IllegalArgumentException {
final T result = getValue(field, type);
return result == null ? defaultValue : result;
}
@Override
public final <T> T getValue(String fieldName, Class<? extends T> type) throws IllegalArgumentException {
return TypeUtils.convert(getValue(fieldName), type);
}
@Override
public final <Z> Z getValue(String fieldName, Class<? extends Z> type, Z defaultValue) throws IllegalArgumentException {
final Z result = getValue(fieldName, type);
return result == null ? defaultValue : result;
}
@Override
public final <T> T into(Class<? extends T> type) {
try {
T result = type.newInstance();
for (Field<?> field : getFields()) {
List<java.lang.reflect.Field> members;
List<java.lang.reflect.Method> 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));
}
}
}

View File

@ -282,6 +282,11 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
return convertToArray(fetchOne());
}
@Override
public final <T> List<T> fetchInto(Class<? extends T> type) throws SQLException {
return fetch().into(type);
}
private final Object[] convertToArray(R record) {
final List<Field<?>> fields = record.getFields();
Object[] array = new Object[fields.size()];

View File

@ -244,6 +244,17 @@ abstract class AbstractStore<T> implements Store<T>, AttachableInternal {
return result == null ? defaultValue : result;
}
@Override
public final <Z> Z getValue(int index, Class<? extends Z> type) throws IllegalArgumentException {
return TypeUtils.convert(getValue(index), type);
}
@Override
public final <Z> Z getValue(int index, Class<? extends Z> type, Z defaultValue) throws IllegalArgumentException {
final Z result = getValue(index, type);
return result == null ? defaultValue : result;
}
// -------------------------------------------------------------------------
// equals and hashCode
// -------------------------------------------------------------------------

View File

@ -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 (<code>javax.persistence</code>) is on the
* classpath.
*/
private static Boolean isJPAAvailable;
/**
* Create a new Oracle-style VARRAY {@link ArrayRecord}
*/
static <R extends ArrayRecord<?>> R newArrayRecord(Class<R> 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 <code>type</code> 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<java.lang.reflect.Field> getAnnotatedMembers(Class<?> type, String name) {
List<java.lang.reflect.Field> result = new ArrayList<java.lang.reflect.Field>();
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<java.lang.reflect.Field> getMatchingMembers(Class<?> type, String name) {
List<java.lang.reflect.Field> result = new ArrayList<java.lang.reflect.Field>();
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<Method> getAnnotatedMethods(Class<?> type, String name) {
List<Method> result = new ArrayList<Method>();
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<Method> getMatchingMethods(Class<?> type, String name) {
List<Method> result = new ArrayList<Method>();
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;
}
}

View File

@ -1171,6 +1171,17 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
new String[] { "&quot;", "&apos;", "&lt;", "&gt;", "&amp;"});
}
@Override
public final <T> List<T> into(Class<? extends T> type) {
List<T> list = new ArrayList<T>();
for (Record record : this) {
list.add(record.into(type));
}
return list;
}
// -------------------------------------------------------------------------
// Object API
// -------------------------------------------------------------------------

View File

@ -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);
}
}