[#1837] Add support for @java.beans.ConstructorProperties when
fetching into immutable POJOs
This commit is contained in:
parent
95a0f0e6b5
commit
b1ff568f41
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2012, 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 java.beans.ConstructorProperties;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public class ImmutableAuthorWithConstructorProperties {
|
||||
|
||||
private final int id;
|
||||
private final String firstName;
|
||||
private final String lastName;
|
||||
private final Date dateOfBirth;
|
||||
|
||||
// Check if setAccessible is called correctly
|
||||
@ConstructorProperties({ "firstName", "lastName", "id", "dateOfBirth" })
|
||||
ImmutableAuthorWithConstructorProperties(String firstName, String lastName, int id, Date dateOfBirth) {
|
||||
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.dateOfBirth = dateOfBirth;
|
||||
}
|
||||
|
||||
// This should never be called
|
||||
@SuppressWarnings("unused")
|
||||
ImmutableAuthorWithConstructorProperties(int ID, String firstName, String lastName) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public Date getDateOfBirth() {
|
||||
return dateOfBirth;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2012, 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 java.beans.ConstructorProperties;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public class ImmutableAuthorWithConstructorPropertiesAndJPAAnnotations {
|
||||
|
||||
private final String f1;
|
||||
private final String f2;
|
||||
private final int f3;
|
||||
private final Date f4;
|
||||
|
||||
// Check if setAccessible is called correctly
|
||||
@ConstructorProperties({ "f1", "f2", "f3", "f4" })
|
||||
ImmutableAuthorWithConstructorPropertiesAndJPAAnnotations(String firstName, String lastName, int id,
|
||||
Date dateOfBirth) {
|
||||
|
||||
this.f1 = firstName;
|
||||
this.f2 = lastName;
|
||||
this.f3 = id;
|
||||
this.f4 = dateOfBirth;
|
||||
}
|
||||
|
||||
// This should never be called
|
||||
@SuppressWarnings("unused")
|
||||
ImmutableAuthorWithConstructorPropertiesAndJPAAnnotations(int ID, String firstName, String lastName) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Column(name = "FIRST_NAME")
|
||||
public String getF1() {
|
||||
return f1;
|
||||
}
|
||||
|
||||
@Column(name = "LAST_NAME")
|
||||
public String getF2() {
|
||||
return f2;
|
||||
}
|
||||
|
||||
@Column(name = "ID")
|
||||
public int getF3() {
|
||||
return f3;
|
||||
}
|
||||
|
||||
@Column(name = "DATE_OF_BIRTH")
|
||||
public Date getF4() {
|
||||
return f4;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2012, 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 java.beans.ConstructorProperties;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public class ImmutableAuthorWithConstructorPropertiesAndJPAAnnotationsAndPublicFields {
|
||||
|
||||
@Column(name = "FIRST_NAME")
|
||||
public final String f1;
|
||||
|
||||
@Column(name = "LAST_NAME")
|
||||
public final String f2;
|
||||
|
||||
@Column(name = "ID")
|
||||
public final int f3;
|
||||
|
||||
@Column(name = "DATE_OF_BIRTH")
|
||||
public final Date f4;
|
||||
|
||||
// Check if setAccessible is called correctly
|
||||
@ConstructorProperties({ "f1", "f2", "f3", "f4" })
|
||||
ImmutableAuthorWithConstructorPropertiesAndJPAAnnotationsAndPublicFields(String firstName, String lastName, int id,
|
||||
Date dateOfBirth) {
|
||||
|
||||
this.f1 = firstName;
|
||||
this.f2 = lastName;
|
||||
this.f3 = id;
|
||||
this.f4 = dateOfBirth;
|
||||
}
|
||||
|
||||
// This should never be called
|
||||
@SuppressWarnings("unused")
|
||||
ImmutableAuthorWithConstructorPropertiesAndJPAAnnotationsAndPublicFields(int ID, String firstName, String lastName) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2012, 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 java.beans.ConstructorProperties;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public class ImmutableAuthorWithConstructorPropertiesAndPublicFields {
|
||||
|
||||
public final String firstName;
|
||||
public final String lastName;
|
||||
public final int id;
|
||||
public final Date dateOfBirth;
|
||||
|
||||
// Check if setAccessible is called correctly
|
||||
@ConstructorProperties({ "firstName", "lastName", "id", "dateOfBirth" })
|
||||
ImmutableAuthorWithConstructorPropertiesAndPublicFields(String firstName, String lastName, int id, Date dateOfBirth) {
|
||||
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.id = id;
|
||||
this.dateOfBirth = dateOfBirth;
|
||||
}
|
||||
|
||||
// This should never be called
|
||||
@SuppressWarnings("unused")
|
||||
ImmutableAuthorWithConstructorPropertiesAndPublicFields(int ID, String firstName, String lastName) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
@ -91,6 +91,10 @@ import org.jooq.test._.FinalWithoutAnnotations;
|
||||
import org.jooq.test._.IBookWithAnnotations;
|
||||
import org.jooq.test._.IBookWithoutAnnotations;
|
||||
import org.jooq.test._.ImmutableAuthor;
|
||||
import org.jooq.test._.ImmutableAuthorWithConstructorProperties;
|
||||
import org.jooq.test._.ImmutableAuthorWithConstructorPropertiesAndJPAAnnotations;
|
||||
import org.jooq.test._.ImmutableAuthorWithConstructorPropertiesAndJPAAnnotationsAndPublicFields;
|
||||
import org.jooq.test._.ImmutableAuthorWithConstructorPropertiesAndPublicFields;
|
||||
import org.jooq.test._.StaticWithAnnotations;
|
||||
import org.jooq.test._.StaticWithoutAnnotations;
|
||||
|
||||
@ -981,6 +985,59 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658,
|
||||
catch (MappingException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflectionWithImmutablesAndConstructorProperties() throws Exception {
|
||||
|
||||
// TODO [#791] Fix test data and have all upper case columns everywhere
|
||||
switch (getDialect()) {
|
||||
case ASE:
|
||||
case CUBRID:
|
||||
case INGRES:
|
||||
case POSTGRES:
|
||||
log.info("SKIPPING", "fetchInto() tests");
|
||||
return;
|
||||
}
|
||||
|
||||
Record author =
|
||||
create().select(
|
||||
TAuthor_LAST_NAME(),
|
||||
TAuthor_FIRST_NAME(),
|
||||
TAuthor_ID(),
|
||||
TAuthor_YEAR_OF_BIRTH())
|
||||
.from(TAuthor())
|
||||
.where(TAuthor_ID().equal(1))
|
||||
.fetchOne();
|
||||
|
||||
ImmutableAuthorWithConstructorProperties into1 =
|
||||
author.into(ImmutableAuthorWithConstructorProperties.class);
|
||||
assertNull(into1.getDateOfBirth());
|
||||
assertEquals(1, into1.getId());
|
||||
assertEquals(AUTHOR_FIRST_NAMES.get(0), into1.getFirstName());
|
||||
assertEquals(AUTHOR_LAST_NAMES.get(0), into1.getLastName());
|
||||
|
||||
ImmutableAuthorWithConstructorPropertiesAndPublicFields into2 =
|
||||
author.into(ImmutableAuthorWithConstructorPropertiesAndPublicFields.class);
|
||||
assertNull(into2.dateOfBirth);
|
||||
assertEquals(1, into2.id);
|
||||
assertEquals(AUTHOR_FIRST_NAMES.get(0), into2.firstName);
|
||||
assertEquals(AUTHOR_LAST_NAMES.get(0), into2.lastName);
|
||||
|
||||
ImmutableAuthorWithConstructorPropertiesAndJPAAnnotations into3 =
|
||||
author.into(ImmutableAuthorWithConstructorPropertiesAndJPAAnnotations.class);
|
||||
assertNull(into3.getF4());
|
||||
assertEquals(1, into3.getF3());
|
||||
assertEquals(AUTHOR_FIRST_NAMES.get(0), into3.getF1());
|
||||
assertEquals(AUTHOR_LAST_NAMES.get(0), into3.getF2());
|
||||
|
||||
ImmutableAuthorWithConstructorPropertiesAndJPAAnnotationsAndPublicFields into4 =
|
||||
author.into(ImmutableAuthorWithConstructorPropertiesAndJPAAnnotationsAndPublicFields.class);
|
||||
assertNull(into4.f4);
|
||||
assertEquals(1, into4.f3);
|
||||
assertEquals(AUTHOR_FIRST_NAMES.get(0), into4.f1);
|
||||
assertEquals(AUTHOR_LAST_NAMES.get(0), into4.f2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFetchIntoTableRecords() throws Exception {
|
||||
jOOQAbstractTest.reset = false;
|
||||
|
||||
@ -1119,6 +1119,11 @@ public abstract class jOOQAbstractTest<
|
||||
new FetchTests(this).testReflectionWithImmutables();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflectionWithImmutablesAndConstructorProperties() throws Exception {
|
||||
new FetchTests(this).testReflectionWithImmutablesAndConstructorProperties();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchIntoTableRecords() throws Exception {
|
||||
new FetchTests(this).testFetchIntoTableRecords();
|
||||
|
||||
@ -4447,7 +4447,8 @@ notExists(create.selectOne().from(BOOK)
|
||||
(DATE '2010-01-01', DATE '2010-01-03') OVERLAPS (DATE '2010-01-02' DATE '2010-01-04')
|
||||
|
||||
-- INTERVAL data types are also supported. This is equivalent to the above
|
||||
(DATE '2010-01-01', CAST('+2 00:00:00' AS INTERVAL DAY TO SECOND)) OVERLAPS (DATE '2010-01-02', CAST('+2 00:00:00' AS INTERVAL DAY TO SECOND))]]></sql>
|
||||
(DATE '2010-01-01', CAST('+2 00:00:00' AS INTERVAL DAY TO SECOND)) OVERLAPS
|
||||
(DATE '2010-01-02', CAST('+2 00:00:00' AS INTERVAL DAY TO SECOND))]]></sql>
|
||||
|
||||
<h3>The OVERLAPS predicate in jOOQ</h3>
|
||||
<p>
|
||||
@ -5494,9 +5495,18 @@ public class MyBook1 {
|
||||
// The various "into()" methods allow for fetching records into your custom POJOs:
|
||||
MyBook1 myBook = create.select().from(BOOK).fetchAny().into(MyBook1.class);
|
||||
List<MyBook1> myBooks = create.select().from(BOOK).fetch().into(MyBook1.class);
|
||||
List<MyBook1> myBooks = create.select().from(BOOK).fetchInto(MyBook1.class);
|
||||
List<MyBook1> myBooks = create.select().from(BOOK).fetchInto(MyBook1.class);]]></java>
|
||||
|
||||
<p>
|
||||
Please refer to the <reference class="org.jooq.Record" anchor="#into(java.lang.Class)" title="Record.into()"/> Javadoc for more details.
|
||||
</p>
|
||||
|
||||
// An "immutable" POJO class
|
||||
<h3>Using "immutable" POJOs</h3>
|
||||
<p>
|
||||
If jOOQ does not find any default constructor, columns are mapped to the "best-matching" constructor. This allows for using "immutable" POJOs with jOOQ. An example illustrates this:
|
||||
</p>
|
||||
|
||||
<java><![CDATA[// An "immutable" POJO class
|
||||
public class MyBook2 {
|
||||
public final int id;
|
||||
public final String title;
|
||||
@ -5510,7 +5520,26 @@ public class MyBook2 {
|
||||
// With "immutable" POJO classes, there must be an exact match between projected fields and available constructors:
|
||||
MyBook2 myBook = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchAny().into(MyBook2.class);
|
||||
List<MyBook2> myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetch().into(MyBook2.class);
|
||||
List<MyBook2> myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchInto(MyBook2.class);]]></java>
|
||||
List<MyBook2> myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchInto(MyBook2.class);
|
||||
|
||||
// An "immutable" POJO class with a java.beans.ConstructorProperties annotation
|
||||
public class MyBook3 {
|
||||
public final String title;
|
||||
public final int id;
|
||||
|
||||
@ConstructorProperties({ "title", "id"})
|
||||
public MyBook2(String title, int id) {
|
||||
this.title = title;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
// With annotated "immutable" POJO classes, there doesn't need to be an exact match between fields and constructor arguments.
|
||||
// In the below cases, only BOOK.ID is really set onto the POJO, BOOK.TITLE remains null and BOOK.AUTHOR_ID is ignored
|
||||
MyBook3 myBook = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetchAny().into(MyBook3.class);
|
||||
List<MyBook3> myBooks = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetch().into(MyBook3.class);
|
||||
List<MyBook3> myBooks = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetchInto(MyBook3.class);
|
||||
]]></java>
|
||||
|
||||
<p>
|
||||
Please refer to the <reference class="org.jooq.Record" anchor="#into(java.lang.Class)" title="Record.into()"/> Javadoc for more details.
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
|
||||
package org.jooq;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.math.BigDecimal;
|
||||
@ -965,7 +966,8 @@ public interface Record extends FieldProvider, Store<Object> {
|
||||
* that might have occurred
|
||||
* @see Convert#convert(Object, Converter)
|
||||
*/
|
||||
<T, U> U getValue(Field<T> field, Converter<? super T, U> converter) throws IllegalArgumentException, DataTypeException;
|
||||
<T, U> U getValue(Field<T> field, Converter<? super T, U> converter) throws IllegalArgumentException,
|
||||
DataTypeException;
|
||||
|
||||
/**
|
||||
* Get a converted value from this record, providing a field.
|
||||
@ -983,8 +985,8 @@ public interface Record extends FieldProvider, Store<Object> {
|
||||
* that might have occurred
|
||||
* @see Convert#convert(Object, Converter)
|
||||
*/
|
||||
<T, U> U getValue(Field<T> field, Converter<? super T, U> converter, U defaultValue) throws IllegalArgumentException,
|
||||
DataTypeException;
|
||||
<T, U> U getValue(Field<T> field, Converter<? super T, U> converter, U defaultValue)
|
||||
throws IllegalArgumentException, DataTypeException;
|
||||
|
||||
/**
|
||||
* Get a converted value from this Record, providing a field name.
|
||||
@ -1175,6 +1177,23 @@ public interface Record extends FieldProvider, Store<Object> {
|
||||
* <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>If no default constructor is available, but at least one constructor
|
||||
* annotated with <code>ConstructorProperties</code> is available, that one
|
||||
* is used</h3>
|
||||
* <ul>
|
||||
* <li>The standard JavaBeans {@link ConstructorProperties} annotation is
|
||||
* used to match constructor arguments against POJO members or getters.</li>
|
||||
* <li>If those POJO members or getters have JPA annotations, those will be
|
||||
* used according to the aforementioned rules, in order to map
|
||||
* <code>Record</code> values onto constructor arguments.</li>
|
||||
* <li>If those POJO members or getters don't have JPA annotations, the
|
||||
* aforementioned naming conventions will be used, in order to map
|
||||
* <code>Record</code> values onto constructor arguments.</li>
|
||||
* <li>When several annotated constructors are found, the first one is
|
||||
* chosen (as reported by {@link Class#getDeclaredConstructors()}</li>
|
||||
* <li>When invoking the annotated constructor, values are converted onto
|
||||
* constructor argument types</li>
|
||||
* </ul>
|
||||
* <h3>If no default constructor is available, but at least one "matching"
|
||||
* constructor is available, that one is used</h3>
|
||||
* <ul>
|
||||
@ -1182,7 +1201,8 @@ public interface Record extends FieldProvider, Store<Object> {
|
||||
* this record holds fields</li>
|
||||
* <li>When several "matching" constructors are found, the first one is
|
||||
* chosen (as reported by {@link Class#getDeclaredConstructors()}</li>
|
||||
* <li>When invoking the "matching"
|
||||
* <li>When invoking the "matching" constructor, values are converted onto
|
||||
* constructor argument types</li>
|
||||
* </ul>
|
||||
* <h3>If the supplied type is an interface or an abstract class</h3>
|
||||
* Abstract types are instanciated using Java reflection {@link Proxy}
|
||||
@ -1293,12 +1313,11 @@ public interface Record extends FieldProvider, Store<Object> {
|
||||
* <ul>
|
||||
* <li>primitive types are supported.</li>
|
||||
* </ul>
|
||||
* <h3>General notes</h3>
|
||||
* The resulting record will have its internal "changed" flags set to true
|
||||
* for all values. This means that {@link UpdatableRecord#store()} will
|
||||
* perform an <code>INSERT</code> statement. If you wish to store the record
|
||||
* using an <code>UPDATE</code> statement, use
|
||||
* {@link Factory#executeUpdate(UpdatableRecord)} instead.
|
||||
* <h3>General notes</h3> The resulting record will have its internal
|
||||
* "changed" flags set to true for all values. This means that
|
||||
* {@link UpdatableRecord#store()} will perform an <code>INSERT</code>
|
||||
* statement. If you wish to store the record using an <code>UPDATE</code>
|
||||
* statement, use {@link Factory#executeUpdate(UpdatableRecord)} instead.
|
||||
*
|
||||
* @param source The source object to copy data from
|
||||
* @throws MappingException wrapping any reflection exception that might
|
||||
|
||||
@ -42,8 +42,11 @@ 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.getPropertyName;
|
||||
import static org.jooq.impl.Util.hasColumnAnnotations;
|
||||
import static org.jooq.tools.reflect.Reflect.accessible;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
@ -54,6 +57,7 @@ import java.sql.Date;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -715,7 +719,7 @@ abstract class AbstractRecord extends AbstractStore<Object> implements Record {
|
||||
|
||||
// [#1340] Allow for using non-public default constructors
|
||||
else {
|
||||
result = Reflect.accessible(type.getDeclaredConstructor()).newInstance();
|
||||
result = accessible(type.getDeclaredConstructor()).newInstance();
|
||||
}
|
||||
|
||||
return intoMutablePOJO(type, result);
|
||||
@ -734,19 +738,83 @@ abstract class AbstractRecord extends AbstractStore<Object> implements Record {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private final <E> E intoImmutablePOJO(Class<? extends E> type) throws Exception {
|
||||
for (Constructor<E> constructor : (Constructor<E>[]) type.getDeclaredConstructors()) {
|
||||
Constructor<E>[] constructors = (Constructor<E>[]) type.getDeclaredConstructors();
|
||||
|
||||
// [#1837] If any java.beans.ConstructorProperties annotations are
|
||||
// present use those rather than matching constructors by the number of
|
||||
// arguments
|
||||
for (Constructor<E> constructor : constructors) {
|
||||
ConstructorProperties properties = constructor.getAnnotation(ConstructorProperties.class);
|
||||
|
||||
if (properties != null) {
|
||||
return intoImmutablePOJO(type, constructor, properties);
|
||||
}
|
||||
}
|
||||
|
||||
// Without ConstructorProperties, match constructors by matching
|
||||
// argument length
|
||||
for (Constructor<E> constructor : constructors) {
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
|
||||
// Match the first constructor by parameter length
|
||||
if (parameterTypes.length == getFields().size()) {
|
||||
Object[] converted = Util.convert(parameterTypes, intoArray());
|
||||
return Reflect.accessible(constructor).newInstance(converted);
|
||||
return accessible(constructor).newInstance(converted);
|
||||
}
|
||||
}
|
||||
|
||||
throw new MappingException("No matching constructor found on type " + type + " for record " + this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an immutable POJO given a constructor and its associated JavaBeans
|
||||
* {@link ConstructorProperties}
|
||||
*/
|
||||
private final <E> E intoImmutablePOJO(Class<? extends E> type, Constructor<E> constructor, ConstructorProperties properties) throws Exception {
|
||||
boolean useAnnotations = hasColumnAnnotations(type);
|
||||
List<String> propertyNames = Arrays.asList(properties.value());
|
||||
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
Object[] parameterValues = new Object[parameterTypes.length];
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
for (java.lang.reflect.Field member : members) {
|
||||
int index = propertyNames.indexOf(member.getName());
|
||||
|
||||
if (index >= 0) {
|
||||
parameterValues[index] = getValue(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (method != null) {
|
||||
String name = getPropertyName(method.getName());
|
||||
int index = propertyNames.indexOf(name);
|
||||
|
||||
if (index >= 0) {
|
||||
parameterValues[index] = getValue(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object[] converted = Util.convert(parameterTypes, parameterValues);
|
||||
return accessible(constructor).newInstance(converted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this record into a "mutable" POJO (non-final fields or setters
|
||||
* available)
|
||||
|
||||
@ -988,6 +988,25 @@ final class Util {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property name associated with a getter/setter method name.
|
||||
*/
|
||||
static String getPropertyName(String methodName) {
|
||||
String name = methodName;
|
||||
|
||||
if (name.startsWith("is") && name.length() > 2) {
|
||||
name = name.substring(2, 3).toLowerCase() + name.substring(3);
|
||||
}
|
||||
else if (name.startsWith("get") && name.length() > 3) {
|
||||
name = name.substring(3, 4).toLowerCase() + name.substring(4);
|
||||
}
|
||||
else if (name.startsWith("set") && name.length() > 3) {
|
||||
name = name.substring(3, 4).toLowerCase() + name.substring(4);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safely copy a value from one record to another
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user