[#198] Add SELECT INTO functionality
[#782] Add <T> T Record.getValue(..., Class<? extends T>) methods for convenient type conversion
This commit is contained in:
parent
d190cc7b60
commit
2e7ea84cd1
@ -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);
|
||||
|
||||
@ -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}"/>
|
||||
|
||||
@ -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>
|
||||
|
||||
Binary file not shown.
107
jOOQ-test/src/org/jooq/test/BookWithAnnotations.java
Normal file
107
jOOQ-test/src/org/jooq/test/BookWithAnnotations.java
Normal 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 + "]";
|
||||
}
|
||||
}
|
||||
83
jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java
Normal file
83
jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java
Normal 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 + "]";
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
|
||||
@ -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>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()];
|
||||
|
||||
@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1171,6 +1171,17 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
|
||||
new String[] { """, "'", "<", ">", "&"});
|
||||
}
|
||||
|
||||
@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
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user