From 16efd8d2fb39fae7e15dfea4cc342666b5bc2c4d Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Sat, 12 Nov 2011 13:05:57 +0000 Subject: [PATCH] [#912] Add R newRecord(Table, Object) as the inverse of various into(Class) methods [#932] Let the bound of R in TableRecord extend TableRecord, in UpdatableRecord to extend UpdatableRecord [#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 --- .../org/jooq/util/spring/FactoryProxy.java | 7 +- .../src/org/jooq/test/{ => $}/BookRecord.java | 2 +- .../src/org/jooq/test/{ => $}/BookTable.java | 2 +- .../test/{ => $}/BookWithAnnotations.java | 15 +- .../test/{ => $}/BookWithoutAnnotations.java | 6 +- .../test/{ => $}/DatesWithAnnotations.java | 2 +- .../org/jooq/test/$/FinalWithAnnotations.java | 48 ++++++ .../jooq/test/$/FinalWithoutAnnotations.java | 46 ++++++ .../jooq/test/$/StaticWithAnnotations.java | 57 +++++++ .../jooq/test/$/StaticWithoutAnnotations.java | 52 ++++++ .../src/org/jooq/test/jOOQAbstractTest.java | 130 ++++++++++++++- jOOQ/src/main/java/org/jooq/Cursor.java | 18 +-- .../main/java/org/jooq/FactoryOperations.java | 17 +- jOOQ/src/main/java/org/jooq/Record.java | 94 ++++++++--- jOOQ/src/main/java/org/jooq/Result.java | 10 +- jOOQ/src/main/java/org/jooq/ResultQuery.java | 6 +- jOOQ/src/main/java/org/jooq/TableRecord.java | 2 +- .../main/java/org/jooq/UpdatableRecord.java | 2 +- ...toException.java => MappingException.java} | 6 +- .../java/org/jooq/impl/AbstractQuery.java | 4 +- .../java/org/jooq/impl/AbstractQueryPart.java | 2 +- .../java/org/jooq/impl/AbstractRecord.java | 148 ++++++++++++++---- .../java/org/jooq/impl/AbstractRoutine.java | 6 +- .../org/jooq/impl/AbstractStoreQuery.java | 2 +- .../java/org/jooq/impl/AbstractSubSelect.java | 4 +- .../java/org/jooq/impl/AliasProviderImpl.java | 2 +- .../java/org/jooq/impl/BatchMultiple.java | 4 +- .../main/java/org/jooq/impl/BatchSingle.java | 4 +- .../main/java/org/jooq/impl/CursorImpl.java | 6 +- jOOQ/src/main/java/org/jooq/impl/Decode.java | 2 +- .../org/jooq/impl/DefaultBindContext.java | 6 +- .../main/java/org/jooq/impl/Expression.java | 2 +- jOOQ/src/main/java/org/jooq/impl/Factory.java | 18 ++- .../java/org/jooq/impl/FieldTypeHelper.java | 4 +- .../java/org/jooq/impl/InsertQueryImpl.java | 2 +- jOOQ/src/main/java/org/jooq/impl/Join.java | 2 +- .../main/java/org/jooq/impl/LoaderImpl.java | 2 +- .../org/jooq/impl/MetaDataFieldProvider.java | 2 +- .../main/java/org/jooq/impl/SQLCondition.java | 2 +- .../src/main/java/org/jooq/impl/SQLField.java | 2 +- .../src/main/java/org/jooq/impl/SQLQuery.java | 2 +- .../java/org/jooq/impl/SQLResultQuery.java | 2 +- .../src/main/java/org/jooq/impl/SQLTable.java | 2 +- .../org/jooq/impl/UpdatableRecordImpl.java | 3 +- .../jooq/impl/{JooqUtil.java => Util.java} | 140 +++++++++++++++-- 45 files changed, 757 insertions(+), 140 deletions(-) rename jOOQ-test/src/org/jooq/test/{ => $}/BookRecord.java (96%) rename jOOQ-test/src/org/jooq/test/{ => $}/BookTable.java (96%) rename jOOQ-test/src/org/jooq/test/{ => $}/BookWithAnnotations.java (90%) rename jOOQ-test/src/org/jooq/test/{ => $}/BookWithoutAnnotations.java (94%) rename jOOQ-test/src/org/jooq/test/{ => $}/DatesWithAnnotations.java (96%) create mode 100644 jOOQ-test/src/org/jooq/test/$/FinalWithAnnotations.java create mode 100644 jOOQ-test/src/org/jooq/test/$/FinalWithoutAnnotations.java create mode 100644 jOOQ-test/src/org/jooq/test/$/StaticWithAnnotations.java create mode 100644 jOOQ-test/src/org/jooq/test/$/StaticWithoutAnnotations.java rename jOOQ/src/main/java/org/jooq/exception/{FetchIntoException.java => MappingException.java} (91%) rename jOOQ/src/main/java/org/jooq/impl/{JooqUtil.java => Util.java} (74%) diff --git a/jOOQ-spring/src/main/java/org/jooq/util/spring/FactoryProxy.java b/jOOQ-spring/src/main/java/org/jooq/util/spring/FactoryProxy.java index 38e16da244..5590c5dbce 100644 --- a/jOOQ-spring/src/main/java/org/jooq/util/spring/FactoryProxy.java +++ b/jOOQ-spring/src/main/java/org/jooq/util/spring/FactoryProxy.java @@ -324,10 +324,15 @@ public class FactoryProxy implements FactoryOperations, MethodInterceptor { } @Override - public final R newRecord(Table table) { + public final > R newRecord(Table table) { return getDelegate().newRecord(table); } + @Override + public final > R newRecord(Table table, Object source) { + return getDelegate().newRecord(table, source); + } + @Override public final Result fetch(ResultSet rs) { return getDelegate().fetch(rs); diff --git a/jOOQ-test/src/org/jooq/test/BookRecord.java b/jOOQ-test/src/org/jooq/test/$/BookRecord.java similarity index 96% rename from jOOQ-test/src/org/jooq/test/BookRecord.java rename to jOOQ-test/src/org/jooq/test/$/BookRecord.java index b4948e92e0..342fd94148 100644 --- a/jOOQ-test/src/org/jooq/test/BookRecord.java +++ b/jOOQ-test/src/org/jooq/test/$/BookRecord.java @@ -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; diff --git a/jOOQ-test/src/org/jooq/test/BookTable.java b/jOOQ-test/src/org/jooq/test/$/BookTable.java similarity index 96% rename from jOOQ-test/src/org/jooq/test/BookTable.java rename to jOOQ-test/src/org/jooq/test/$/BookTable.java index 69c86677e3..9a94cdb6f2 100644 --- a/jOOQ-test/src/org/jooq/test/BookTable.java +++ b/jOOQ-test/src/org/jooq/test/$/BookTable.java @@ -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; diff --git a/jOOQ-test/src/org/jooq/test/BookWithAnnotations.java b/jOOQ-test/src/org/jooq/test/$/BookWithAnnotations.java similarity index 90% rename from jOOQ-test/src/org/jooq/test/BookWithAnnotations.java rename to jOOQ-test/src/org/jooq/test/$/BookWithAnnotations.java index 15aabcbe23..a0d4b921d7 100644 --- a/jOOQ-test/src/org/jooq/test/BookWithAnnotations.java +++ b/jOOQ-test/src/org/jooq/test/$/BookWithAnnotations.java @@ -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") diff --git a/jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java b/jOOQ-test/src/org/jooq/test/$/BookWithoutAnnotations.java similarity index 94% rename from jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java rename to jOOQ-test/src/org/jooq/test/$/BookWithoutAnnotations.java index ce33250a9c..a47107113d 100644 --- a/jOOQ-test/src/org/jooq/test/BookWithoutAnnotations.java +++ b/jOOQ-test/src/org/jooq/test/$/BookWithoutAnnotations.java @@ -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) { diff --git a/jOOQ-test/src/org/jooq/test/DatesWithAnnotations.java b/jOOQ-test/src/org/jooq/test/$/DatesWithAnnotations.java similarity index 96% rename from jOOQ-test/src/org/jooq/test/DatesWithAnnotations.java rename to jOOQ-test/src/org/jooq/test/$/DatesWithAnnotations.java index 3eeea0628c..438b4c55e3 100644 --- a/jOOQ-test/src/org/jooq/test/DatesWithAnnotations.java +++ b/jOOQ-test/src/org/jooq/test/$/DatesWithAnnotations.java @@ -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; diff --git a/jOOQ-test/src/org/jooq/test/$/FinalWithAnnotations.java b/jOOQ-test/src/org/jooq/test/$/FinalWithAnnotations.java new file mode 100644 index 0000000000..f1dcf50616 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/$/FinalWithAnnotations.java @@ -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; + +} diff --git a/jOOQ-test/src/org/jooq/test/$/FinalWithoutAnnotations.java b/jOOQ-test/src/org/jooq/test/$/FinalWithoutAnnotations.java new file mode 100644 index 0000000000..1c0d02877b --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/$/FinalWithoutAnnotations.java @@ -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; + +} diff --git a/jOOQ-test/src/org/jooq/test/$/StaticWithAnnotations.java b/jOOQ-test/src/org/jooq/test/$/StaticWithAnnotations.java new file mode 100644 index 0000000000..2361b23aee --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/$/StaticWithAnnotations.java @@ -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; + } +} diff --git a/jOOQ-test/src/org/jooq/test/$/StaticWithoutAnnotations.java b/jOOQ-test/src/org/jooq/test/$/StaticWithoutAnnotations.java new file mode 100644 index 0000000000..259c192907 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/$/StaticWithoutAnnotations.java @@ -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; + } +} diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 17b3f5169b..40c3438822 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -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: diff --git a/jOOQ/src/main/java/org/jooq/Cursor.java b/jOOQ/src/main/java/org/jooq/Cursor.java index 037c3612a4..12a0594611 100644 --- a/jOOQ/src/main/java/org/jooq/Cursor.java +++ b/jOOQ/src/main/java/org/jooq/Cursor.java @@ -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 extends FieldProvider, Iterable { * @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 fetchOneInto(Class type) throws DataAccessException, FetchIntoException; + E fetchOneInto(Class type) throws DataAccessException, MappingException; /** * Map resulting records onto a custom type. @@ -151,10 +151,10 @@ public interface Cursor extends FieldProvider, Iterable { * @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 */ - List fetchInto(Class type) throws DataAccessException, FetchIntoException; + List fetchInto(Class type) throws DataAccessException, MappingException; /** * Map the next resulting record onto a custom record. @@ -167,10 +167,10 @@ public interface Cursor extends FieldProvider, Iterable { * @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 fetchOneInto(Table table) throws DataAccessException, FetchIntoException; + > Z fetchOneInto(Table table) throws DataAccessException, MappingException; /** * Map resulting records onto a custom record. @@ -183,10 +183,10 @@ public interface Cursor extends FieldProvider, Iterable { * @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 */ - > List fetchInto(Table table) throws DataAccessException, FetchIntoException; + > List fetchInto(Table table) throws DataAccessException, MappingException; /** * Explicitly close the underlying {@link PreparedStatement} and diff --git a/jOOQ/src/main/java/org/jooq/FactoryOperations.java b/jOOQ/src/main/java/org/jooq/FactoryOperations.java index 39e1de624b..d65587b04e 100644 --- a/jOOQ/src/main/java/org/jooq/FactoryOperations.java +++ b/jOOQ/src/main/java/org/jooq/FactoryOperations.java @@ -662,7 +662,22 @@ public interface FactoryOperations extends Configuration { * @param table The table holding records of type <R> * @return The new record */ - R newRecord(Table table); + > R newRecord(Table table); + + /** + * Create a new pre-filled {@link Record} that can be inserted into the + * corresponding table. + *

+ * This performs roughly the inverse operation of {@link Record#into(Class)} + * + * @param The generic record type + * @param table The table holding records of type <R> + * @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 newRecord(Table table, Object source); // ------------------------------------------------------------------------- // Fast querying diff --git a/jOOQ/src/main/java/org/jooq/Record.java b/jOOQ/src/main/java/org/jooq/Record.java index b6c5f0821a..9fee1966fc 100644 --- a/jOOQ/src/main/java/org/jooq/Record.java +++ b/jOOQ/src/main/java/org/jooq/Record.java @@ -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 { *

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

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

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

If a field's - * value for {@link Field#getName()} is MY_field - * (case-sensitive!), then this field's value will be set on all of these: + * map Record values by naming convention: If + * {@link Field#getName()} is MY_field (case-sensitive!), then + * this field's value will be set on all of these: *
    - *
  • Public member MY_field
  • - *
  • Public member myField
  • - *
  • Public method MY_field(...)
  • - *
  • Public method myField(...)
  • - *
  • Public method setMY_field(...)
  • - *
  • Public method setMyField(...)
  • + *
  • Public single-argument instance method MY_field(...)
  • + *
  • Public single-argument instance method myField(...)
  • + *
  • Public single-argument instance method setMY_field(...)
  • + *
  • Public single-argument instance method setMyField(...)
  • + *
  • Public non-final instance member field MY_field
  • + *
  • Public non-final instance member field myField
  • *
*

Other restrictions

*
    @@ -898,11 +903,12 @@ public interface Record extends FieldProvider, Store { * numbers, or false for booleans). Hence, there is no way of * distinguishing null and 0 in that case. * - * + * * @param The generic entity type. * @param type The entity type. + * @see #from(Object) */ - E into(Class type) throws FetchIntoException; + E into(Class 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 { * default constructors are made accessible using * {@link Constructor#setAccessible(boolean)} * - * + * * @param The generic table record type. * @param table The table type. */ > R into(Table table); + + /** + * Load data into this record from a source. The mapping algorithm is this: + *

    If any JPA {@link Column} annotations are found on the {@link Class} + * of the provided source, only those are used. Matching + * candidates are:

    + *
      + *
    • Public no-argument instance methods annotated with + * Column
    • + *
    • Public no-argument instance methods starting with getXXX + * or isXXX, if there exists a matching public single-argument + * setXXX() instance method that is annotated with + * Column
    • + *
    • Public instance member fields annotated with Column
    • + *
    + * Additional matching rules: + *
      + *
    • {@link Column#name()} must match {@link Field#getName()}. All other + * annotation attributes are ignored
    • + *
    • Only the first match per field is used
    • + *
    • Matching methods have a higher priority than matching member fields
    • + *
    • Explicitly matching methods have a higher priority than implicitly + * matching methods (implicitly matching getter = setter is annotated)
    • + *
    • Static methods / member fields are ignored
    • + *
    + *

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

    If {@link Field#getName()} is + * MY_field (case-sensitive!), then this field's value will be + * fetched from the first of these: + *
      + *
    • Public no-argument instance method MY_field()
    • + *
    • Public no-argument instance method myField()
    • + *
    • Public no-argument instance method getMY_field()
    • + *
    • Public no-argument instance method getMyField()
    • + *
    • Public instance member field MY_field
    • + *
    • Public instance member field myField
    • + *
    + *

    Other restrictions

    + *
      + *
    • primitive types are supported.
    • + *
    + * + * @param source The source object to copy data from + * @see #into(Class) + */ + void from(Object source) throws MappingException; + } diff --git a/jOOQ/src/main/java/org/jooq/Result.java b/jOOQ/src/main/java/org/jooq/Result.java index b1326fc9e1..7bd01705db 100644 --- a/jOOQ/src/main/java/org/jooq/Result.java +++ b/jOOQ/src/main/java/org/jooq/Result.java @@ -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 extends FieldProvider, List, Attach * * @param 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) */ - List into(Class type) throws FetchIntoException; + List into(Class type) throws MappingException; /** * Map resulting records onto a custom record. @@ -1742,11 +1742,11 @@ public interface Result extends FieldProvider, List, Attach * * @param 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) */ - > Result into(Table table) throws FetchIntoException; + > Result into(Table table) throws MappingException; /** * Map results into a custom handler callback diff --git a/jOOQ/src/main/java/org/jooq/ResultQuery.java b/jOOQ/src/main/java/org/jooq/ResultQuery.java index 135498472a..a8ba955398 100644 --- a/jOOQ/src/main/java/org/jooq/ResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/ResultQuery.java @@ -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 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 */ - List fetchInto(Class type) throws DataAccessException, FetchIntoException; + List fetchInto(Class type) throws DataAccessException, MappingException; /** * Map resulting records onto a custom record. diff --git a/jOOQ/src/main/java/org/jooq/TableRecord.java b/jOOQ/src/main/java/org/jooq/TableRecord.java index 6f9abf6225..97f87a78c8 100644 --- a/jOOQ/src/main/java/org/jooq/TableRecord.java +++ b/jOOQ/src/main/java/org/jooq/TableRecord.java @@ -43,7 +43,7 @@ import org.jooq.exception.DataAccessException; * @param The record type * @author Lukas Eder */ -public interface TableRecord extends Record { +public interface TableRecord> extends Record { /** * The table from which this record was read diff --git a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java index 617e354de7..c20b1481fa 100644 --- a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java +++ b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java @@ -68,7 +68,7 @@ import org.jooq.exception.DataAccessException; * @param The record type * @author Lukas Eder */ -public interface UpdatableRecord extends Updatable, TableRecord { +public interface UpdatableRecord> extends Updatable, TableRecord { /** * The table from which this record was read diff --git a/jOOQ/src/main/java/org/jooq/exception/FetchIntoException.java b/jOOQ/src/main/java/org/jooq/exception/MappingException.java similarity index 91% rename from jOOQ/src/main/java/org/jooq/exception/FetchIntoException.java rename to jOOQ/src/main/java/org/jooq/exception/MappingException.java index af7a381fc1..fe0220c331 100644 --- a/jOOQ/src/main/java/org/jooq/exception/FetchIntoException.java +++ b/jOOQ/src/main/java/org/jooq/exception/MappingException.java @@ -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); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java index eb33e5da69..7527ba9af8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java @@ -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"); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java index 540d39c447..367d76f132 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQueryPart.java @@ -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); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index fd39f03f4d..900889d0c1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -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 implements Record { public final T into(Class type) { try { T result = type.newInstance(); + boolean useAnnotations = isJPAAvailable() && hasColumnAnnotations(type); for (Field field : getFields()) { List members; List methods; // Annotations are available and present - if (JooqUtil.isJPAAvailable() && JooqUtil.hasColumnAnnotations(type)) { - members = JooqUtil.getAnnotatedMembers(type, field.getName()); - methods = JooqUtil.getAnnotatedMethods(type, field.getName()); + 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 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 into(Table 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 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 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 implements Record { } } - @Override - public final > R into(Table 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 void setValue(Record target, Field sourceField, Field targetField) { - target.setValue(targetField, targetField.getDataType().convert(getValue(sourceField))); - } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index b1c07955de..7da4c46fda 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -290,7 +290,7 @@ public abstract class AbstractRoutine 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 extends AbstractSchemaProviderQueryPart // also the type name case ORACLE: { if (sqlType == Types.STRUCT) { - UDTRecord record = JooqUtil.newRecord((Class>) parameter.getType()); + UDTRecord record = Util.newRecord((Class>) parameter.getType()); statement.registerOutParameter(index, Types.STRUCT, record.getSQLTypeName()); } else if (sqlType == Types.ARRAY) { - ArrayRecord record = JooqUtil.newArrayRecord( + ArrayRecord record = Util.newArrayRecord( (Class>) parameter.getType(), configuration); statement.registerOutParameter(index, Types.ARRAY, record.getName()); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java index 89c56c0cdb..087a71625e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java @@ -121,7 +121,7 @@ abstract class AbstractStoreQuery> extends AbstractQuer } else { try { - A record = JooqUtil.newArrayRecord(field.getType(), getConfiguration()); + A record = Util.newArrayRecord(field.getType(), getConfiguration()); record.setList(value); addValue(field, record); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractSubSelect.java b/jOOQ/src/main/java/org/jooq/impl/AbstractSubSelect.java index 197fb02e3b..857da4b0ee 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractSubSelect.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractSubSelect.java @@ -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; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/AliasProviderImpl.java b/jOOQ/src/main/java/org/jooq/impl/AliasProviderImpl.java index 3f0d6cff88..049bac5dd7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AliasProviderImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/AliasProviderImpl.java @@ -117,7 +117,7 @@ class AliasProviderImpl> extends AbstractNamedQueryPa ArrayTable table = (ArrayTable) o; context.sql("("); - JooqUtil.toSQLNames(context, table.getFields()); + Util.toSQLNames(context, table.getFields()); context.sql(")"); } diff --git a/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java b/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java index 5d49f719b5..4dc2d75d1a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java +++ b/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java @@ -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"); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java b/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java index 090aea5079..bad699bca7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java +++ b/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java @@ -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"); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index 51534acb9d..4024a3597d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -191,7 +191,7 @@ class CursorImpl implements Cursor { @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 implements Cursor { try { if (!isClosed && rs.next()) { - record = JooqUtil.newRecord(type, fields, configuration); + record = Util.newRecord(type, fields, configuration); final List> fieldList = fields.getFields(); final int size = fieldList.size(); @@ -260,7 +260,7 @@ class CursorImpl implements Cursor { } } 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 diff --git a/jOOQ/src/main/java/org/jooq/impl/Decode.java b/jOOQ/src/main/java/org/jooq/impl/Decode.java index 0661b516f0..9f85f99e28 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Decode.java +++ b/jOOQ/src/main/java/org/jooq/impl/Decode.java @@ -55,7 +55,7 @@ class Decode extends AbstractFunction { private final Field[] more; public Decode(Field field, Field search, Field 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; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java index c0569874b4..2b0ae228ac 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBindContext.java @@ -68,7 +68,7 @@ class DefaultBindContext extends AbstractContext 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 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 implements BindCon // Treat Oracle-style ARRAY types specially if (ArrayRecord.class.isAssignableFrom(type)) { - String typeName = JooqUtil.newArrayRecord((Class>) type, configuration).getName(); + String typeName = Util.newArrayRecord((Class>) type, configuration).getName(); stmt.setNull(nextIndex(), sqlType, typeName); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index 6f35fefa9d..2503208396 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -84,7 +84,7 @@ class Expression extends AbstractFunction { private final ExpressionOperator operator; Expression(ExpressionOperator operator, Field 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; diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index 13c027753c..8fe5d80952 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -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(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 newRecord(Table table) { - return JooqUtil.newRecord(table, this); + public final > R newRecord(Table table) { + return Util.newRecord(table, this); + } + + /** + * {@inheritDoc} + */ + @Override + public final > R newRecord(Table table, Object source) { + R result = newRecord(table); + result.from(source); + return result; } // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java b/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java index e7c827e2d1..180279b6ff 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldTypeHelper.java @@ -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 values = new PGobjectParser().parse(object.toString()); List> fields = record.getFields(); diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java index 9247d75510..c7e0afc4ac 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java @@ -463,7 +463,7 @@ class InsertQueryImpl> extends AbstractStoreQuery 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(id)); getReturnedRecords().add(typed); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Join.java b/jOOQ/src/main/java/org/jooq/impl/Join.java index ca53b7c922..0944354048 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Join.java +++ b/jOOQ/src/main/java/org/jooq/impl/Join.java @@ -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 { diff --git a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java index 87427f49f8..ff7a7ff3b1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java @@ -434,7 +434,7 @@ class LoaderImpl> 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(); diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java b/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java index f68bdb4921..0f16330a3e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java +++ b/jOOQ/src/main/java/org/jooq/impl/MetaDataFieldProvider.java @@ -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; diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java b/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java index 87eb9301d0..e6b8509bd7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java @@ -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 diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLField.java b/jOOQ/src/main/java/org/jooq/impl/SQLField.java index 0f1cfc1b84..9f0e6c3714 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLField.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLField.java @@ -67,7 +67,7 @@ class SQLField extends AbstractField { @Override public final void toSQL(RenderContext context) { - JooqUtil.toSQLReference(context, sql, bindings); + Util.toSQLReference(context, sql, bindings); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java b/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java index 289e9bcefe..8a43e8e417 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java @@ -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 diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java index cef2c884f6..b04d4ab15d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java @@ -70,7 +70,7 @@ class SQLResultQuery extends AbstractResultQuery { @Override public final void toSQL(RenderContext context) { - JooqUtil.toSQLReference(context, sql, bindings); + Util.toSQLReference(context, sql, bindings); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLTable.java b/jOOQ/src/main/java/org/jooq/impl/SQLTable.java index ef0e1cebb9..c496e3ad63 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLTable.java @@ -78,7 +78,7 @@ class SQLTable extends AbstractTable { @Override public final void toSQL(RenderContext context) { - JooqUtil.toSQLReference(context, sql, bindings); + Util.toSQLReference(context, sql, bindings); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java index 8e15a11bad..773254b7db 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java @@ -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> extends TableRecordImpl implements UpdatableRecord { +public class UpdatableRecordImpl> extends TableRecordImpl implements UpdatableRecord { /** * Generated UID diff --git a/jOOQ/src/main/java/org/jooq/impl/JooqUtil.java b/jOOQ/src/main/java/org/jooq/impl/Util.java similarity index 74% rename from jOOQ/src/main/java/org/jooq/impl/JooqUtil.java rename to jOOQ/src/main/java/org/jooq/impl/Util.java index 79e56178a1..073a91786a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JooqUtil.java +++ b/jOOQ/src/main/java/org/jooq/impl/Util.java @@ -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 (javax.persistence) 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 getAnnotatedMembers(Class type, String name) { List result = new ArrayList(); - 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 getMatchingMembers(Class type, String name) { List result = new ArrayList(); - 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 getAnnotatedMethods(Class type, String name) { + static final List getAnnotatedSetters(Class type, String name) { List result = new ArrayList(); - 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 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 getMatchingSetters(Class type, String name) { List result = new ArrayList(); - 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 getInstanceMethods(Class type) { + List result = new ArrayList(); + + for (Method method : type.getMethods()) { + if ((method.getModifiers() & Modifier.STATIC) == 0) { + result.add(method); + } + } + + return result; + } + + private static final List getInstanceMembers(Class type) { + List result = new ArrayList(); + + 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 void setValue(Record target, Field targetField, Record source, Field sourceField) { + setValue(target, targetField, source.getValue(sourceField)); + } + + /** + * Type-safely set a value to a record + */ + static final void setValue(Record target, Field targetField, Object value) { + target.setValue(targetField, targetField.getDataType().convert(value)); + } +} \ No newline at end of file