[#912] Add <R extends Record> R newRecord(Table<R>, Object) as the inverse of various into(Class<?>) methods

[#932] Let the bound of R in TableRecord extend TableRecord<R>, in UpdatableRecord to extend UpdatableRecord<R>
[#934] Don't consider static members in reflection utilities when used with Record.into(Class<?>) and similar methods
[#935] Don't consider final member fields in reflection utilities when used with Record.into(Class<?>) and similar methods
This commit is contained in:
Lukas Eder 2011-11-12 13:05:57 +00:00
parent 37802d86d8
commit 16efd8d2fb
45 changed files with 757 additions and 140 deletions

View File

@ -324,10 +324,15 @@ public class FactoryProxy implements FactoryOperations, MethodInterceptor {
}
@Override
public final <R extends Record> R newRecord(Table<R> table) {
public final <R extends TableRecord<R>> R newRecord(Table<R> table) {
return getDelegate().newRecord(table);
}
@Override
public final <R extends TableRecord<R>> R newRecord(Table<R> table, Object source) {
return getDelegate().newRecord(table, source);
}
@Override
public final Result<Record> fetch(ResultSet rs) {
return getDelegate().fetch(rs);

View File

@ -33,7 +33,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.test;
package org.jooq.test.$;
import org.jooq.impl.CustomRecord;

View File

@ -33,7 +33,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.test;
package org.jooq.test.$;
import org.jooq.TableField;
import org.jooq.impl.CustomTable;

View File

@ -33,7 +33,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.test;
package org.jooq.test.$;
import javax.persistence.Column;
@ -47,6 +47,9 @@ public class BookWithAnnotations {
@Column(name = "ID")
public Integer id;
@Column(name = "ID")
public Long id4;
@Column(name = "ID")
public int id2;
@ -62,6 +65,7 @@ public class BookWithAnnotations {
// Members without annotations
// ---------------------------
public int id3;
public long id5;
public String firstName2;
public String lastName;
public String lastName2;
@ -69,8 +73,13 @@ public class BookWithAnnotations {
// Methods with annotations
// ------------------------
@Column(name = "ID")
public void setId(int id) {
id3 = id;
public void setId(long id) {
id3 = (int) id;
}
@Column(name = "ID")
public void setId(Long id) {
id5 = (int) (long) id;
}
@Column(name = "FIRST_NAME")

View File

@ -33,7 +33,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.test;
package org.jooq.test.$;
/**
@ -53,8 +53,8 @@ public class BookWithoutAnnotations {
public java.util.Date DATE_OF_BIRTH;
public java.sql.Date dateOfBirth;
public void setId(int id) {
id2 = id;
public void setId(long id) {
id2 = (int) id;
}
public void setFirstName(String f) {

View File

@ -33,7 +33,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.test;
package org.jooq.test.$;
import java.util.Calendar;
import java.util.Date;

View File

@ -0,0 +1,48 @@
/**
* 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 FinalWithAnnotations {
@Column(name = "ID")
public final int ID = 13;
}

View File

@ -0,0 +1,46 @@
/**
* 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 FinalWithoutAnnotations {
public final int ID = 13;
}

View File

@ -0,0 +1,57 @@
/**
* 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 StaticWithAnnotations {
@Column(name = "ID")
public static int ID = 13;
@Column(name = "ID")
public static int getID() {
return ID;
}
@Column(name = "ID")
public static void setID(int id) {
ID = id;
}
}

View File

@ -0,0 +1,52 @@
/**
* 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 StaticWithoutAnnotations {
public static int ID = 13;
public static int getID() {
return ID;
}
public static void setID(int id) {
ID = id;
}
}

View File

@ -132,12 +132,21 @@ import org.jooq.UpdatableTable;
import org.jooq.UpdateQuery;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DetachedException;
import org.jooq.exception.FetchIntoException;
import org.jooq.exception.InvalidResultException;
import org.jooq.exception.MappingException;
import org.jooq.impl.CustomCondition;
import org.jooq.impl.CustomField;
import org.jooq.impl.Factory;
import org.jooq.impl.SQLDataType;
import org.jooq.test.$.BookRecord;
import org.jooq.test.$.BookTable;
import org.jooq.test.$.BookWithAnnotations;
import org.jooq.test.$.BookWithoutAnnotations;
import org.jooq.test.$.DatesWithAnnotations;
import org.jooq.test.$.FinalWithAnnotations;
import org.jooq.test.$.FinalWithoutAnnotations;
import org.jooq.test.$.StaticWithAnnotations;
import org.jooq.test.$.StaticWithoutAnnotations;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StopWatch;
import org.jooq.tools.StringUtils;
@ -2716,6 +2725,16 @@ public abstract class jOOQAbstractTest<
assertEquals(3, result.get(2).id3);
assertEquals(4, result.get(3).id3);
assertEquals(Long.valueOf(1), result.get(0).id4);
assertEquals(Long.valueOf(2), result.get(1).id4);
assertEquals(Long.valueOf(3), result.get(2).id4);
assertEquals(Long.valueOf(4), result.get(3).id4);
assertEquals(1L, result.get(0).id5);
assertEquals(2L, result.get(1).id5);
assertEquals(3L, result.get(2).id5);
assertEquals(4L, result.get(3).id5);
assertEquals("1984", result.get(0).title);
assertEquals("Animal Farm", result.get(1).title);
assertEquals("O Alquimista", result.get(2).title);
@ -2747,7 +2766,7 @@ public abstract class jOOQAbstractTest<
.fetchInto(AbstractList.class);
fail();
}
catch (FetchIntoException expected) {}
catch (MappingException expected) {}
try {
// Cannot a class without default constructor
@ -2755,7 +2774,7 @@ public abstract class jOOQAbstractTest<
.fetchInto(Math.class);
fail();
}
catch (FetchIntoException expected) {}
catch (MappingException expected) {}
// [#930] Calendar/Date conversion checks
// --------------------------------------
@ -2891,8 +2910,113 @@ public abstract class jOOQAbstractTest<
assertEquals("Coelho", result.get(3).LAST_NAME);
}
@Test
public void testRecordFromWithAnnotations() throws Exception {
BookWithAnnotations b = new BookWithAnnotations();
b.firstName = "Edgar Allen";
b.lastName2 = "Poe";
b.dateOfBirth = new Date(1);
b.id = 17;
b.title = "The Raven";
// This data shouldn't be considered
b.id2 = 18;
b.lastName = "Poet";
B book = create().newRecord(TBook(), b);
A author = create().newRecord(TAuthor(), b);
assertEquals(b.id, author.getValue(TAuthor_ID()));
assertEquals(b.firstName, author.getValue(TAuthor_FIRST_NAME()));
assertEquals(b.lastName2, author.getValue(TAuthor_LAST_NAME()));
assertEquals(b.dateOfBirth, author.getValue(TAuthor_DATE_OF_BIRTH()));
assertNull(author.getValue(TAuthor_YEAR_OF_BIRTH()));
assertEquals(b.id, book.getValue(TBook_ID()));
assertEquals(b.title, book.getValue(TBook_TITLE()));
assertNull(book.getValue(TBook_AUTHOR_ID()));
assertNull(book.getValue(TBook_CONTENT_PDF()));
assertNull(book.getValue(TBook_CONTENT_TEXT()));
assertNull(book.getValue(TBook_LANGUAGE_ID()));
assertNull(book.getValue(TBook_PUBLISHED_IN()));
}
@Test
public void testRecordFromWithoutAnnotations() throws Exception {
BookWithoutAnnotations b = new BookWithoutAnnotations();
b.firstName = "Edgar Allen";
b.lastName = "Poe";
b.DATE_OF_BIRTH = new Date(1);
b.id = 17;
b.title = "The Raven";
// This data shouldn't be considered
b.id2 = 18;
b.ID = 19;
b.LAST_NAME = "Poet";
b.dateOfBirth = new Date(2);
B book = create().newRecord(TBook(), b);
A author = create().newRecord(TAuthor(), b);
assertEquals(b.id, author.getValue(TAuthor_ID()));
assertEquals(b.firstName, author.getValue(TAuthor_FIRST_NAME()));
assertEquals(b.lastName, author.getValue(TAuthor_LAST_NAME()));
assertEquals(b.DATE_OF_BIRTH, author.getValue(TAuthor_DATE_OF_BIRTH()));
assertNull(author.getValue(TAuthor_YEAR_OF_BIRTH()));
assertEquals(b.id, book.getValue(TBook_ID()));
assertEquals(b.title, book.getValue(TBook_TITLE()));
assertNull(book.getValue(TBook_AUTHOR_ID()));
assertNull(book.getValue(TBook_CONTENT_PDF()));
assertNull(book.getValue(TBook_CONTENT_TEXT()));
assertNull(book.getValue(TBook_LANGUAGE_ID()));
assertNull(book.getValue(TBook_PUBLISHED_IN()));
}
@Test
public void testReflectionWithAnnotations() throws Exception {
// [#934] Static members are not to be considered
assertEquals(create().newRecord(TBook()), create().newRecord(TBook(), new StaticWithAnnotations()));
create().newRecord(TBook()).into(StaticWithAnnotations.class);
assertEquals(13, StaticWithAnnotations.ID);
// [#935] Final member fields are considered when reading
B book = create().newRecord(TBook());
book.setValue(TBook_ID(), new FinalWithAnnotations().ID);
assertEquals(book, create().newRecord(TBook(), new FinalWithAnnotations()));
// [#935] ... but not when writing
FinalWithAnnotations f = create().newRecord(TBook()).into(FinalWithAnnotations.class);
assertEquals(f.ID, new FinalWithAnnotations().ID);
}
@Test
public void testReflectionWithoutAnnotations() throws Exception {
// Arbitrary sources should have no effect
assertEquals(create().newRecord(TBook()), create().newRecord(TBook(), (Object) null));
assertEquals(create().newRecord(TBook()), create().newRecord(TBook(), new Object()));
// [#934] Static members are not to be considered
assertEquals(create().newRecord(TBook()), create().newRecord(TBook(), new StaticWithoutAnnotations()));
create().newRecord(TBook()).into(StaticWithoutAnnotations.class);
assertEquals(13, StaticWithoutAnnotations.ID);
// [#935] Final member fields are considered when reading
B book = create().newRecord(TBook());
book.setValue(TBook_ID(), new FinalWithoutAnnotations().ID);
assertEquals(book, create().newRecord(TBook(), new FinalWithoutAnnotations()));
// [#935] ... but not when writing
FinalWithoutAnnotations f = create().newRecord(TBook()).into(FinalWithoutAnnotations.class);
assertEquals(f.ID, new FinalWithoutAnnotations().ID);
}
@Test
public void testFetchIntoCustomTable() throws Exception {
// TODO [#791] Fix test data and have all upper case columns everywhere
switch (getDialect()) {
case ASE:

View File

@ -41,7 +41,7 @@ import java.util.Iterator;
import java.util.List;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.FetchIntoException;
import org.jooq.exception.MappingException;
/**
* Cursors allow for lazy, sequential access to an underlying JDBC
@ -135,10 +135,10 @@ public interface Cursor<R extends Record> extends FieldProvider, Iterable<R> {
* @see Record#into(Class)
* @see Result#into(Class)
* @throws DataAccessException if something went wrong executing the query
* @throws FetchIntoException wrapping any reflection exception that might
* @throws MappingException wrapping any reflection exception that might
* have occurred while mapping records
*/
<E> E fetchOneInto(Class<? extends E> type) throws DataAccessException, FetchIntoException;
<E> E fetchOneInto(Class<? extends E> type) throws DataAccessException, MappingException;
/**
* Map resulting records onto a custom type.
@ -151,10 +151,10 @@ public interface Cursor<R extends Record> extends FieldProvider, Iterable<R> {
* @see Record#into(Class)
* @see Result#into(Class)
* @throws DataAccessException if something went wrong executing the query
* @throws FetchIntoException wrapping any reflection exception that might
* @throws MappingException wrapping any reflection exception that might
* have occurred while mapping records
*/
<E> List<E> fetchInto(Class<? extends E> type) throws DataAccessException, FetchIntoException;
<E> List<E> fetchInto(Class<? extends E> type) throws DataAccessException, MappingException;
/**
* Map the next resulting record onto a custom record.
@ -167,10 +167,10 @@ public interface Cursor<R extends Record> extends FieldProvider, Iterable<R> {
* @see Record#into(Class)
* @see Result#into(Class)
* @throws DataAccessException if something went wrong executing the query
* @throws FetchIntoException wrapping any reflection exception that might
* @throws MappingException wrapping any reflection exception that might
* have occurred while mapping records
*/
<Z extends TableRecord<Z>> Z fetchOneInto(Table<Z> table) throws DataAccessException, FetchIntoException;
<Z extends TableRecord<Z>> Z fetchOneInto(Table<Z> table) throws DataAccessException, MappingException;
/**
* Map resulting records onto a custom record.
@ -183,10 +183,10 @@ public interface Cursor<R extends Record> extends FieldProvider, Iterable<R> {
* @see Record#into(Class)
* @see Result#into(Class)
* @throws DataAccessException if something went wrong executing the query
* @throws FetchIntoException wrapping any reflection exception that might
* @throws MappingException wrapping any reflection exception that might
* have occurred while mapping records
*/
<Z extends TableRecord<Z>> List<Z> fetchInto(Table<Z> table) throws DataAccessException, FetchIntoException;
<Z extends TableRecord<Z>> List<Z> fetchInto(Table<Z> table) throws DataAccessException, MappingException;
/**
* Explicitly close the underlying {@link PreparedStatement} and

View File

@ -662,7 +662,22 @@ public interface FactoryOperations extends Configuration {
* @param table The table holding records of type &lt;R&gt;
* @return The new record
*/
<R extends Record> R newRecord(Table<R> table);
<R extends TableRecord<R>> R newRecord(Table<R> table);
/**
* Create a new pre-filled {@link Record} that can be inserted into the
* corresponding table.
* <p>
* This performs roughly the inverse operation of {@link Record#into(Class)}
*
* @param <R> The generic record type
* @param table The table holding records of type &lt;R&gt;
* @param source The source to be used to fill the new record
* @return The new record
* @see Record#from(Object)
* @see Record#into(Class)
*/
<R extends TableRecord<R>> R newRecord(Table<R> table, Object source);
// -------------------------------------------------------------------------
// Fast querying

View File

@ -45,7 +45,7 @@ import java.sql.Timestamp;
import javax.persistence.Column;
import org.jooq.exception.FetchIntoException;
import org.jooq.exception.MappingException;
/**
* A wrapper for database result records returned by
@ -863,30 +863,35 @@ public interface Record extends FieldProvider, Store<Object> {
* <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
* <li>If <code>type</code> contains public single-argument instance 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>If <code>type</code> contains public no-argument instance methods
* starting with <code>getXXX</code> or <code>isXXX</code>, annotated with
* <code>Column</code>, then matching public <code>setXXX()</code> instance
* methods are invoked</li>
* <li>If <code>type</code> contains public instance member fields annotated
* with <code>Column</code>, those members are set</li>
* </ul>
* Additional rules:
* <ul>
* <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>
* <li>Static methods / member fields are ignored</li>
* <li>Final member fields 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:
* map <code>Record</code> values by naming convention:</h3> If
* {@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>
* <li>Public single-argument instance method <code>MY_field(...)</code></li>
* <li>Public single-argument instance method <code>myField(...)</code></li>
* <li>Public single-argument instance method <code>setMY_field(...)</code></li>
* <li>Public single-argument instance method <code>setMyField(...)</code></li>
* <li>Public non-final instance member field <code>MY_field</code></li>
* <li>Public non-final instance member field <code>myField</code></li>
* </ul>
* <h3>Other restrictions</h3>
* <ul>
@ -898,11 +903,12 @@ public interface Record extends FieldProvider, Store<Object> {
* 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>
*
*
* @param <E> The generic entity type.
* @param type The entity type.
* @see #from(Object)
*/
<E> E into(Class<? extends E> type) throws FetchIntoException;
<E> E into(Class<? extends E> type) throws MappingException;
/**
* Map resulting records onto a custom record type. The mapping algorithm is
@ -916,9 +922,57 @@ public interface Record extends FieldProvider, Store<Object> {
* default constructors are made accessible using
* {@link Constructor#setAccessible(boolean)}</li>
* </ul>
*
*
* @param <R> The generic table record type.
* @param table The table type.
*/
<R extends TableRecord<R>> R into(Table<R> table);
/**
* Load data into this record from a source. The mapping algorithm is this:
* <h3>If any JPA {@link Column} annotations are found on the {@link Class}
* of the provided <code>source</code>, only those are used. Matching
* candidates are:</h3>
* <ul>
* <li>Public no-argument instance methods annotated with
* <code>Column</code></li>
* <li>Public no-argument instance methods starting with <code>getXXX</code>
* or <code>isXXX</code>, if there exists a matching public single-argument
* <code>setXXX()</code> instance method that is annotated with
* <code>Column</code></li>
* <li>Public instance member fields annotated with <code>Column</code></li>
* </ul>
* Additional matching rules:
* <ul>
* <li>{@link Column#name()} must match {@link Field#getName()}. All other
* annotation attributes are ignored</li>
* <li>Only the first match per field is used</li>
* <li>Matching methods have a higher priority than matching member fields</li>
* <li>Explicitly matching methods have a higher priority than implicitly
* matching methods (implicitly matching getter = setter is annotated)</li>
* <li>Static methods / member fields 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 members by naming convention:</h3> If {@link Field#getName()} is
* <code>MY_field</code> (case-sensitive!), then this field's value will be
* fetched from the first of these:
* <ul>
* <li>Public no-argument instance method <code>MY_field()</code></li>
* <li>Public no-argument instance method <code>myField()</code></li>
* <li>Public no-argument instance method <code>getMY_field()</code></li>
* <li>Public no-argument instance method <code>getMyField()</code></li>
* <li>Public instance member field <code>MY_field</code></li>
* <li>Public instance member field <code>myField</code></li>
* </ul>
* <h3>Other restrictions</h3>
* <ul>
* <li>primitive types are supported.</li>
* </ul>
*
* @param source The source object to copy data from
* @see #into(Class)
*/
void from(Object source) throws MappingException;
}

View File

@ -43,7 +43,7 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.util.List;
import org.jooq.exception.FetchIntoException;
import org.jooq.exception.MappingException;
import org.w3c.dom.Document;
@ -1727,11 +1727,11 @@ public interface Result<R extends Record> extends FieldProvider, List<R>, Attach
*
* @param <E> The generic entity type.
* @param type The entity type.
* @throws FetchIntoException wrapping any reflection exception that might
* @throws MappingException wrapping any reflection exception that might
* have occurred while mapping records
* @see Record#into(Class)
*/
<E> List<E> into(Class<? extends E> type) throws FetchIntoException;
<E> List<E> into(Class<? extends E> type) throws MappingException;
/**
* Map resulting records onto a custom record.
@ -1742,11 +1742,11 @@ public interface Result<R extends Record> extends FieldProvider, List<R>, Attach
*
* @param <Z> The generic table record type.
* @param table The table type.
* @throws FetchIntoException wrapping any reflection exception that might
* @throws MappingException wrapping any reflection exception that might
* have occurred while mapping records
* @see Record#into(Table)
*/
<Z extends TableRecord<Z>> Result<Z> into(Table<Z> table) throws FetchIntoException;
<Z extends TableRecord<Z>> Result<Z> into(Table<Z> table) throws MappingException;
/**
* Map results into a custom handler callback

View File

@ -43,7 +43,7 @@ import java.util.Map;
import java.util.concurrent.ExecutorService;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.FetchIntoException;
import org.jooq.exception.MappingException;
/**
* A query that can return results. Mostly, this is a {@link Select} query used
@ -443,10 +443,10 @@ public interface ResultQuery<R extends Record> extends Query {
* @see Record#into(Class)
* @see Result#into(Class)
* @throws DataAccessException if something went wrong executing the query
* @throws FetchIntoException wrapping any reflection exception that might
* @throws MappingException wrapping any reflection exception that might
* have occurred while mapping records
*/
<E> List<E> fetchInto(Class<? extends E> type) throws DataAccessException, FetchIntoException;
<E> List<E> fetchInto(Class<? extends E> type) throws DataAccessException, MappingException;
/**
* Map resulting records onto a custom record.

View File

@ -43,7 +43,7 @@ import org.jooq.exception.DataAccessException;
* @param <R> The record type
* @author Lukas Eder
*/
public interface TableRecord<R extends Record> extends Record {
public interface TableRecord<R extends TableRecord<R>> extends Record {
/**
* The table from which this record was read

View File

@ -68,7 +68,7 @@ import org.jooq.exception.DataAccessException;
* @param <R> The record type
* @author Lukas Eder
*/
public interface UpdatableRecord<R extends Record> extends Updatable<R>, TableRecord<R> {
public interface UpdatableRecord<R extends UpdatableRecord<R>> extends Updatable<R>, TableRecord<R> {
/**
* The table from which this record was read

View File

@ -52,7 +52,7 @@ import org.jooq.ResultQuery;
*
* @author Lukas Eder
*/
public class FetchIntoException extends DataAccessException {
public class MappingException extends DataAccessException {
/**
* Generated UID
@ -64,7 +64,7 @@ public class FetchIntoException extends DataAccessException {
*
* @param message the detail message
*/
public FetchIntoException(String message) {
public MappingException(String message) {
super(message);
}
@ -75,7 +75,7 @@ public class FetchIntoException extends DataAccessException {
* @param cause the root cause (usually from using a underlying data access
* API such as JDBC)
*/
public FetchIntoException(String message, Throwable cause) {
public MappingException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -103,11 +103,11 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query {
return result;
}
catch (SQLException e) {
throw JooqUtil.translate("AbstractQuery.execute", sql, e);
throw Util.translate("AbstractQuery.execute", sql, e);
}
finally {
if (!keepStatementOpen()) {
JooqUtil.safeClose(statement);
Util.safeClose(statement);
}
watch.splitDebug("Statement executed");

View File

@ -276,6 +276,6 @@ abstract class AbstractQueryPart implements QueryPartInternal, AttachableInterna
* Internal convenience method
*/
protected final DataAccessException translate(String task, String sql, SQLException e) {
return JooqUtil.translate(task, sql, e);
return Util.translate(task, sql, e);
}
}

View File

@ -36,8 +36,18 @@
package org.jooq.impl;
import static org.jooq.impl.Util.getAnnotatedGetter;
import static org.jooq.impl.Util.getAnnotatedMembers;
import static org.jooq.impl.Util.getAnnotatedSetters;
import static org.jooq.impl.Util.getMatchingGetter;
import static org.jooq.impl.Util.getMatchingMembers;
import static org.jooq.impl.Util.getMatchingSetters;
import static org.jooq.impl.Util.hasColumnAnnotations;
import static org.jooq.impl.Util.isJPAAvailable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
@ -54,7 +64,7 @@ import org.jooq.FieldProvider;
import org.jooq.Record;
import org.jooq.Table;
import org.jooq.TableRecord;
import org.jooq.exception.FetchIntoException;
import org.jooq.exception.MappingException;
/**
* @author Lukas Eder
@ -487,29 +497,34 @@ abstract class AbstractRecord extends AbstractStore<Object> implements Record {
public final <T> T into(Class<? extends T> type) {
try {
T result = type.newInstance();
boolean useAnnotations = isJPAAvailable() && hasColumnAnnotations(type);
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());
if (useAnnotations) {
members = getAnnotatedMembers(type, field.getName());
methods = getAnnotatedSetters(type, field.getName());
}
// No annotations are present
else {
members = JooqUtil.getMatchingMembers(type, field.getName());
methods = JooqUtil.getMatchingMethods(type, field.getName());
members = getMatchingMembers(type, field.getName());
methods = getMatchingSetters(type, field.getName());
}
for (java.lang.reflect.Field member : members) {
copyInto(result, member, field);
// [#935] Avoid setting final fields
if ((member.getModifiers() & Modifier.FINAL) == 0) {
into(result, member, field);
}
}
for (java.lang.reflect.Method method : methods) {
copyInto(result, method, field);
into(result, method, field);
}
}
@ -518,11 +533,74 @@ abstract class AbstractRecord extends AbstractStore<Object> implements Record {
// All reflection exceptions are intercepted
catch (Exception e) {
throw new FetchIntoException("An error ocurred when mapping record to " + type, e);
throw new MappingException("An error ocurred when mapping record to " + type, e);
}
}
private final void copyInto(Object result, Method method, Field<?> field)
@Override
public final <R extends TableRecord<R>> R into(Table<R> table) {
try {
R result = Util.newRecord(table, getConfiguration());
for (Field<?> sourceField : getFields()) {
Field<?> targetField = result.getField(sourceField);
if (targetField != null) {
Util.setValue(result, targetField, this, sourceField);
}
}
return result;
}
// All reflection exceptions are intercepted
catch (Exception e) {
throw new MappingException("An error ocurred when mapping record to " + table, e);
}
}
@Override
public final void from(Object source) {
if (source == null) return;
Class<?> type = source.getClass();
try {
boolean useAnnotations = isJPAAvailable() && hasColumnAnnotations(type);
for (Field<?> field : getFields()) {
List<java.lang.reflect.Field> members;
Method method;
// Annotations are available and present
if (useAnnotations) {
members = getAnnotatedMembers(type, field.getName());
method = getAnnotatedGetter(type, field.getName());
}
// No annotations are present
else {
members = getMatchingMembers(type, field.getName());
method = getMatchingGetter(type, field.getName());
}
// Use only the first applicable method or member
if (method != null) {
Util.setValue(this, field, method.invoke(source));
}
else if (members.size() > 0) {
from(source, members.get(0), field);
}
}
}
// All reflection exceptions are intercepted
catch (Exception e) {
throw new MappingException("An error ocurred when mapping record from " + type, e);
}
}
private final void into(Object result, Method method, Field<?> field)
throws IllegalAccessException, InvocationTargetException {
Class<?> mType = method.getParameterTypes()[0];
@ -555,7 +633,7 @@ abstract class AbstractRecord extends AbstractStore<Object> implements Record {
}
}
private final void copyInto(Object result, java.lang.reflect.Field member, Field<?> field) throws IllegalAccessException {
private final void into(Object result, java.lang.reflect.Field member, Field<?> field) throws IllegalAccessException {
Class<?> mType = member.getType();
if (mType.isPrimitive()) {
@ -586,32 +664,36 @@ abstract class AbstractRecord extends AbstractStore<Object> implements Record {
}
}
@Override
public final <R extends TableRecord<R>> R into(Table<R> table) {
try {
R result = JooqUtil.newRecord(table, getConfiguration());
private final void from(Object source, java.lang.reflect.Field member, Field<?> field)
throws IllegalAccessException {
for (Field<?> field : getFields()) {
Field<?> targetField = result.getField(field);
Class<?> mType = member.getType();
if (targetField != null) {
setValue(result, field, targetField);
}
if (mType.isPrimitive()) {
if (mType == byte.class) {
Util.setValue(this, field, member.getByte(source));
}
else if (mType == short.class) {
Util.setValue(this, field, member.getShort(source));
}
else if (mType == int.class) {
Util.setValue(this, field, member.getInt(source));
}
else if (mType == long.class) {
Util.setValue(this, field, member.getLong(source));
}
else if (mType == float.class) {
Util.setValue(this, field, member.getFloat(source));
}
else if (mType == double.class) {
Util.setValue(this, field, member.getDouble(source));
}
else if (mType == boolean.class) {
Util.setValue(this, field, member.getBoolean(source));
}
return result;
}
// All reflection exceptions are intercepted
catch (Exception e) {
throw new FetchIntoException("An error ocurred when mapping record to " + table, e);
else {
Util.setValue(this, field, member.get(source));
}
}
/**
* Type-safely copy a value from one record to another
*/
final <T> void setValue(Record target, Field<?> sourceField, Field<T> targetField) {
target.setValue(targetField, targetField.getDataType().convert(getValue(sourceField)));
}
}

View File

@ -290,7 +290,7 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
throw translate("AbstractRoutine.executeCallableStatement", sql, exc);
}
finally {
JooqUtil.safeClose(statement);
Util.safeClose(statement);
watch.splitDebug("Routine executed");
}
}
@ -408,12 +408,12 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
// also the type name
case ORACLE: {
if (sqlType == Types.STRUCT) {
UDTRecord<?> record = JooqUtil.newRecord((Class<? extends UDTRecord<?>>) parameter.getType());
UDTRecord<?> record = Util.newRecord((Class<? extends UDTRecord<?>>) parameter.getType());
statement.registerOutParameter(index, Types.STRUCT, record.getSQLTypeName());
}
else if (sqlType == Types.ARRAY) {
ArrayRecord<?> record = JooqUtil.newArrayRecord(
ArrayRecord<?> record = Util.newArrayRecord(
(Class<? extends ArrayRecord<?>>) parameter.getType(), configuration);
statement.registerOutParameter(index, Types.ARRAY, record.getName());
}

View File

@ -121,7 +121,7 @@ abstract class AbstractStoreQuery<R extends TableRecord<R>> extends AbstractQuer
}
else {
try {
A record = JooqUtil.newArrayRecord(field.getType(), getConfiguration());
A record = Util.newArrayRecord(field.getType(), getConfiguration());
record.setList(value);
addValue(field, record);
}

View File

@ -236,7 +236,7 @@ implements
if (!forUpdateOf.isEmpty()) {
context.sql(" of ");
JooqUtil.toSQLNames(context, forUpdateOf);
Util.toSQLNames(context, forUpdateOf);
}
else if (!forUpdateOfTables.isEmpty()) {
context.sql(" of ");
@ -255,7 +255,7 @@ implements
// Render the OF [table-names] clause
default:
JooqUtil.toSQLNames(context, forUpdateOfTables);
Util.toSQLNames(context, forUpdateOfTables);
break;
}
}

View File

@ -117,7 +117,7 @@ class AliasProviderImpl<T extends AliasProvider<T>> extends AbstractNamedQueryPa
ArrayTable<?> table = (ArrayTable<?>) o;
context.sql("(");
JooqUtil.toSQLNames(context, table.getFields());
Util.toSQLNames(context, table.getFields());
context.sql(")");
}

View File

@ -88,10 +88,10 @@ class BatchMultiple implements Batch {
return result;
}
catch (SQLException e) {
throw JooqUtil.translate("BatchMultiple.execute", sql, e);
throw Util.translate("BatchMultiple.execute", sql, e);
}
finally {
JooqUtil.safeClose(statement);
Util.safeClose(statement);
watch.splitDebug("Statement executed");
}
}

View File

@ -104,10 +104,10 @@ class BatchSingle implements BatchBindStep {
return result;
}
catch (SQLException e) {
throw JooqUtil.translate("BatchSingle.execute", sql, e);
throw Util.translate("BatchSingle.execute", sql, e);
}
finally {
JooqUtil.safeClose(statement);
Util.safeClose(statement);
watch.splitDebug("Statement executed");
}
}

View File

@ -191,7 +191,7 @@ class CursorImpl<R extends Record> implements Cursor<R> {
@Override
public final void close() {
JooqUtil.safeClose(rs, stmt);
Util.safeClose(rs, stmt);
rs = null;
stmt = null;
isClosed = true;
@ -246,7 +246,7 @@ class CursorImpl<R extends Record> implements Cursor<R> {
try {
if (!isClosed && rs.next()) {
record = JooqUtil.newRecord(type, fields, configuration);
record = Util.newRecord(type, fields, configuration);
final List<Field<?>> fieldList = fields.getFields();
final int size = fieldList.size();
@ -260,7 +260,7 @@ class CursorImpl<R extends Record> implements Cursor<R> {
}
}
catch (SQLException e) {
throw JooqUtil.translate("Cursor.fetch", null, e);
throw Util.translate("Cursor.fetch", null, e);
}
// Conveniently close cursors and underlying objects after the last

View File

@ -55,7 +55,7 @@ class Decode<T, Z> extends AbstractFunction<Z> {
private final Field<?>[] more;
public Decode(Field<T> field, Field<T> search, Field<Z> result, Field<?>[] more) {
super("decode", result.getDataType(), JooqUtil.combine(field, search, result, more));
super("decode", result.getDataType(), Util.combine(field, search, result, more));
this.field = field;
this.search = search;

View File

@ -68,7 +68,7 @@ class DefaultBindContext extends AbstractContext<BindContext> implements BindCon
* Generated UID
*/
private static final long serialVersionUID = -5457385919209241505L;
private static final JooqLogger log = JooqLogger.getLogger(JooqUtil.class);
private static final JooqLogger log = JooqLogger.getLogger(Util.class);
private final PreparedStatement stmt;
private int index;
@ -169,7 +169,7 @@ class DefaultBindContext extends AbstractContext<BindContext> implements BindCon
return bindValue0(value, type);
}
catch (SQLException e) {
throw JooqUtil.translate("DefaultBindContext.bindValue", null, e);
throw Util.translate("DefaultBindContext.bindValue", null, e);
}
}
@ -193,7 +193,7 @@ class DefaultBindContext extends AbstractContext<BindContext> implements BindCon
// Treat Oracle-style ARRAY types specially
if (ArrayRecord.class.isAssignableFrom(type)) {
String typeName = JooqUtil.newArrayRecord((Class<ArrayRecord<?>>) type, configuration).getName();
String typeName = Util.newArrayRecord((Class<ArrayRecord<?>>) type, configuration).getName();
stmt.setNull(nextIndex(), sqlType, typeName);
}

View File

@ -84,7 +84,7 @@ class Expression<T> extends AbstractFunction<T> {
private final ExpressionOperator operator;
Expression(ExpressionOperator operator, Field<T> lhs, Field<?>... rhs) {
super(operator.toSQL(), lhs.getDataType(), JooqUtil.combine(lhs, rhs));
super(operator.toSQL(), lhs.getDataType(), Util.combine(lhs, rhs));
this.operator = operator;
this.lhs = lhs;

View File

@ -36,7 +36,7 @@
package org.jooq.impl;
import static org.jooq.impl.JooqUtil.combine;
import static org.jooq.impl.Util.combine;
import java.io.IOException;
import java.io.ObjectInputStream;
@ -795,7 +795,7 @@ public class Factory implements FactoryOperations {
return new CursorImpl<Record>(this, fields, rs).fetch();
}
catch (SQLException e) {
throw JooqUtil.translate("Factory.fetch", null, e);
throw Util.translate("Factory.fetch", null, e);
}
}
@ -1149,8 +1149,18 @@ public class Factory implements FactoryOperations {
* {@inheritDoc}
*/
@Override
public final <R extends Record> R newRecord(Table<R> table) {
return JooqUtil.newRecord(table, this);
public final <R extends TableRecord<R>> R newRecord(Table<R> table) {
return Util.newRecord(table, this);
}
/**
* {@inheritDoc}
*/
@Override
public final <R extends TableRecord<R>> R newRecord(Table<R> table, Object source) {
R result = newRecord(table);
result.from(source);
return result;
}
// -------------------------------------------------------------------------

View File

@ -501,7 +501,7 @@ public final class FieldTypeHelper {
}
else {
// TODO: [#523] Use array record meta data instead
ArrayRecord<?> record = JooqUtil.newArrayRecord(type, configuration);
ArrayRecord<?> record = Util.newArrayRecord(type, configuration);
record.set(array);
return record;
}
@ -963,7 +963,7 @@ public final class FieldTypeHelper {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
UDTRecord<?> record = (UDTRecord<?>) JooqUtil.newRecord((Class) type);
UDTRecord<?> record = (UDTRecord<?>) Util.newRecord((Class) type);
List<String> values = new PGobjectParser().parse(object.toString());
List<Field<?>> fields = record.getFields();

View File

@ -463,7 +463,7 @@ class InsertQueryImpl<R extends TableRecord<R>> extends AbstractStoreQuery<R> im
// additional query
if (returning.size() == 1 && returning.get(0).equals(field)) {
for (Number id : ids) {
R typed = JooqUtil.newRecord(getInto(), configuration);
R typed = Util.newRecord(getInto(), configuration);
((AbstractRecord) typed).setValue(field, new Value<Number>(id));
getReturnedRecords().add(typed);
}

View File

@ -144,7 +144,7 @@ class Join extends AbstractQueryPart {
private void toSQL0(RenderContext context) {
if (usingSyntax) {
context.sql(" using (");
JooqUtil.toSQLNames(context, using);
Util.toSQLNames(context, using);
context.sql(")");
}
else {

View File

@ -434,7 +434,7 @@ class LoaderImpl<R extends TableRecord<R>> implements
// SQLExceptions originating from rollbacks or commits are always fatal
// They are propagated, and not swallowed
catch (SQLException e) {
throw JooqUtil.translate("LoaderImpl.executeCSV", null, e);
throw Util.translate("LoaderImpl.executeCSV", null, e);
}
finally {
reader.close();

View File

@ -122,7 +122,7 @@ class MetaDataFieldProvider implements FieldProvider, Serializable {
}
}
catch (SQLException e) {
throw JooqUtil.translate("MetaFieldProvider.init", null, e);
throw Util.translate("MetaFieldProvider.init", null, e);
}
meta = null;

View File

@ -67,7 +67,7 @@ class SQLCondition extends AbstractCondition {
// We have no control over the plain SQL content, hence we MUST put it
// in parentheses to ensure correct semantics
JooqUtil.toSQLReferenceWithParentheses(context, sql, bindings);
Util.toSQLReferenceWithParentheses(context, sql, bindings);
}
@Override

View File

@ -67,7 +67,7 @@ class SQLField<T> extends AbstractField<T> {
@Override
public final void toSQL(RenderContext context) {
JooqUtil.toSQLReference(context, sql, bindings);
Util.toSQLReference(context, sql, bindings);
}
@Override

View File

@ -65,7 +65,7 @@ class SQLQuery extends AbstractQuery {
@Override
public final void toSQL(RenderContext context) {
JooqUtil.toSQLReference(context, sql, bindings);
Util.toSQLReference(context, sql, bindings);
}
@Override

View File

@ -70,7 +70,7 @@ class SQLResultQuery extends AbstractResultQuery<Record> {
@Override
public final void toSQL(RenderContext context) {
JooqUtil.toSQLReference(context, sql, bindings);
Util.toSQLReference(context, sql, bindings);
}
@Override

View File

@ -78,7 +78,7 @@ class SQLTable extends AbstractTable<Record> {
@Override
public final void toSQL(RenderContext context) {
JooqUtil.toSQLReference(context, sql, bindings);
Util.toSQLReference(context, sql, bindings);
}
@Override

View File

@ -43,7 +43,6 @@ import java.util.Set;
import org.jooq.Field;
import org.jooq.Identity;
import org.jooq.Record;
import org.jooq.TableRecord;
import org.jooq.UniqueKey;
import org.jooq.UpdatableRecord;
import org.jooq.UpdatableTable;
@ -55,7 +54,7 @@ import org.jooq.UpdatableTable;
*
* @author Lukas Eder
*/
public class UpdatableRecordImpl<R extends TableRecord<R>> extends TableRecordImpl<R> implements UpdatableRecord<R> {
public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableRecordImpl<R> implements UpdatableRecord<R> {
/**
* Generated UID

View File

@ -37,6 +37,7 @@ package org.jooq.impl;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -65,7 +66,7 @@ import org.jooq.tools.StringUtils;
*
* @author Lukas Eder
*/
final class JooqUtil {
final class Util {
/**
* Indicating whether JPA (<code>javax.persistence</code>) is on the
@ -338,13 +339,13 @@ final class JooqUtil {
return true;
}
for (java.lang.reflect.Field member : type.getFields()) {
for (java.lang.reflect.Field member : getInstanceMembers(type)) {
if (member.getAnnotation(Column.class) != null) {
return true;
}
}
for (Method method : type.getMethods()) {
for (Method method : getInstanceMethods(type)) {
if (method.getAnnotation(Column.class) != null) {
return true;
}
@ -359,7 +360,7 @@ final class JooqUtil {
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()) {
for (java.lang.reflect.Field member : getInstanceMembers(type)) {
Column annotation = member.getAnnotation(Column.class);
if (annotation != null) {
@ -378,7 +379,7 @@ final class JooqUtil {
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()) {
for (java.lang.reflect.Field member : getInstanceMembers(type)) {
if (name.equals(member.getName())) {
result.add(member);
}
@ -391,12 +392,12 @@ final class JooqUtil {
}
/**
* Get all methods annotated with a given column name
* Get all setter methods annotated with a given column name
*/
static final List<Method> getAnnotatedMethods(Class<?> type, String name) {
static final List<Method> getAnnotatedSetters(Class<?> type, String name) {
List<Method> result = new ArrayList<Method>();
for (Method method : type.getMethods()) {
for (Method method : getInstanceMethods(type)) {
Column annotation = method.getAnnotation(Column.class);
if (annotation != null && name.equals(annotation.name())) {
@ -429,12 +430,58 @@ final class JooqUtil {
}
/**
* Get all methods matching a given column name
* Get the first getter method annotated with a given column name
*/
static final List<Method> getMatchingMethods(Class<?> type, String name) {
static final Method getAnnotatedGetter(Class<?> type, String name) {
for (Method method : getInstanceMethods(type)) {
Column annotation = method.getAnnotation(Column.class);
if (annotation != null && name.equals(annotation.name())) {
// Annotated getter
if (method.getParameterTypes().length == 0) {
return method;
}
// Annotated setter with matching getter
else if (method.getParameterTypes().length == 1) {
String m = method.getName();
if (m.startsWith("set")) {
try {
Method getter = type.getMethod("get" + m.substring(3));
// Getter annotation is more relevant
if (getter.getAnnotation(Column.class) == null) {
return getter;
}
}
catch (NoSuchMethodException ignore) {}
try {
Method getter = type.getMethod("is" + m.substring(3));
// Getter annotation is more relevant
if (getter.getAnnotation(Column.class) == null) {
return getter;
}
}
catch (NoSuchMethodException ignore) {}
}
}
}
}
return null;
}
/**
* Get all setter methods matching a given column name
*/
static final List<Method> getMatchingSetters(Class<?> type, String name) {
List<Method> result = new ArrayList<Method>();
for (Method method : type.getMethods()) {
for (Method method : getInstanceMethods(type)) {
if (method.getParameterTypes().length == 1) {
if (name.equals(method.getName())) {
result.add(method);
@ -453,4 +500,73 @@ final class JooqUtil {
return result;
}
}
/**
* Get the first getter method matching a given column name
*/
static final Method getMatchingGetter(Class<?> type, String name) {
for (Method method : getInstanceMethods(type)) {
if (method.getParameterTypes().length == 0) {
if (name.equals(method.getName())) {
return method;
}
else if (StringUtils.toCamelCaseLC(name).equals(method.getName())) {
return method;
}
else if (("get" + name).equals(method.getName())) {
return method;
}
else if (("get" + StringUtils.toCamelCase(name)).equals(method.getName())) {
return method;
}
else if (("is" + name).equals(method.getName())) {
return method;
}
else if (("is" + StringUtils.toCamelCase(name)).equals(method.getName())) {
return method;
}
}
}
return null;
}
private static final List<Method> getInstanceMethods(Class<?> type) {
List<Method> result = new ArrayList<Method>();
for (Method method : type.getMethods()) {
if ((method.getModifiers() & Modifier.STATIC) == 0) {
result.add(method);
}
}
return result;
}
private static final List<java.lang.reflect.Field> getInstanceMembers(Class<?> type) {
List<java.lang.reflect.Field> result = new ArrayList<java.lang.reflect.Field>();
for (java.lang.reflect.Field field : type.getFields()) {
if ((field.getModifiers() & Modifier.STATIC) == 0) {
result.add(field);
}
}
return result;
}
/**
* Type-safely copy a value from one record to another
*/
static final <T> void setValue(Record target, Field<T> targetField, Record source, Field<?> sourceField) {
setValue(target, targetField, source.getValue(sourceField));
}
/**
* Type-safely set a value to a record
*/
static final <T> void setValue(Record target, Field<T> targetField, Object value) {
target.setValue(targetField, targetField.getDataType().convert(value));
}
}