[#1470] Support interface types in ResultQuery.fetchInto(Class),

Result.into(Class), and Record.into(Class) methods, returning a proxy
This commit is contained in:
Lukas Eder 2012-06-01 19:32:50 +02:00
parent 129488582a
commit 430c75f0c2
5 changed files with 255 additions and 85 deletions

View File

@ -0,0 +1,71 @@
/**
* 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 javax.persistence.Column;
/**
* @author Lukas Eder
*/
public interface IBookWithAnnotations {
// Methods with annotations
// ------------------------
@Column(name = "ID")
public void setId(long id);
@Column(name = "ID")
public void setId(Long id);
public long getId();
@Column(name = "FIRST_NAME")
public void setFirstName(String f);
@Column(name = "LAST_NAME")
public void setLastName(String l);
public void setLAST_NAME(String l);
@Column(name = "LAST_NAME")
public String getLAST_NAME();
@Column(name = "LAST_NAME")
public void tooManyParameters(String l, String tooMany);
@Column(name = "LAST_NAME")
public void notEnoughParameters();
}

View File

@ -0,0 +1,51 @@
/**
* 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._;
/**
* @author Lukas Eder
*/
public interface IBookWithoutAnnotations {
public void setId(long id);
public long getId();
public void setFirstName(String f);
public void setLAST_NAME(String l);
public String getLAST_NAME();
public void setLAST_NAME(String l, String tooManyParameters);
public void setLAST_NAME();
}

View File

@ -67,6 +67,7 @@ import org.jooq.Field;
import org.jooq.Record;
import org.jooq.RecordHandler;
import org.jooq.Result;
import org.jooq.Select;
import org.jooq.SelectQuery;
import org.jooq.TableRecord;
import org.jooq.UpdatableRecord;
@ -83,6 +84,8 @@ import org.jooq.test._.CharWithAnnotations;
import org.jooq.test._.DatesWithAnnotations;
import org.jooq.test._.FinalWithAnnotations;
import org.jooq.test._.FinalWithoutAnnotations;
import org.jooq.test._.IBookWithAnnotations;
import org.jooq.test._.IBookWithoutAnnotations;
import org.jooq.test._.ImmutableAuthor;
import org.jooq.test._.StaticWithAnnotations;
import org.jooq.test._.StaticWithoutAnnotations;
@ -296,7 +299,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658,
return;
}
List<BookWithAnnotations> result =
Select<?> select =
create().select(
TBook_ID(),
TBook_TITLE(),
@ -305,60 +308,73 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658,
TAuthor_DATE_OF_BIRTH())
.from(TBook())
.join(TAuthor()).on(TBook_AUTHOR_ID().equal(TAuthor_ID()))
.orderBy(TBook_ID())
.fetchInto(BookWithAnnotations.class);
.orderBy(TBook_ID());
assertEquals(4, result.size());
List<BookWithAnnotations> result1 = select.fetchInto(BookWithAnnotations.class);
List<IBookWithAnnotations> result2 = select.fetchInto(IBookWithAnnotations.class);
assertEquals(1, (int) result.get(0).id);
assertEquals(2, (int) result.get(1).id);
assertEquals(3, (int) result.get(2).id);
assertEquals(4, (int) result.get(3).id);
assertEquals(4, result1.size());
assertEquals(4, result2.size());
assertEquals(1, result.get(0).id2);
assertEquals(2, result.get(1).id2);
assertEquals(3, result.get(2).id2);
assertEquals(4, result.get(3).id2);
assertEquals(1, (int) result1.get(0).id);
assertEquals(2, (int) result1.get(1).id);
assertEquals(3, (int) result1.get(2).id);
assertEquals(4, (int) result1.get(3).id);
assertEquals(1, result.get(0).id3);
assertEquals(2, result.get(1).id3);
assertEquals(3, result.get(2).id3);
assertEquals(4, result.get(3).id3);
assertEquals(1, result1.get(0).id2);
assertEquals(2, result1.get(1).id2);
assertEquals(3, result1.get(2).id2);
assertEquals(4, result1.get(3).id2);
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(1, result1.get(0).id3);
assertEquals(2, result1.get(1).id3);
assertEquals(3, result1.get(2).id3);
assertEquals(4, result1.get(3).id3);
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(Long.valueOf(1), result1.get(0).id4);
assertEquals(Long.valueOf(2), result1.get(1).id4);
assertEquals(Long.valueOf(3), result1.get(2).id4);
assertEquals(Long.valueOf(4), result1.get(3).id4);
assertEquals("1984", result.get(0).title);
assertEquals("Animal Farm", result.get(1).title);
assertEquals("O Alquimista", result.get(2).title);
assertEquals("Brida", result.get(3).title);
assertEquals(1L, result1.get(0).id5);
assertEquals(2L, result1.get(1).id5);
assertEquals(3L, result1.get(2).id5);
assertEquals(4L, result1.get(3).id5);
assertEquals("George", result.get(0).firstName);
assertEquals("George", result.get(1).firstName);
assertEquals("Paulo", result.get(2).firstName);
assertEquals("Paulo", result.get(3).firstName);
assertEquals(1, (int) result2.get(0).getId());
assertEquals(2, (int) result2.get(1).getId());
assertEquals(3, (int) result2.get(2).getId());
assertEquals(4, (int) result2.get(3).getId());
assertEquals("George", result.get(0).firstName2);
assertEquals("George", result.get(1).firstName2);
assertEquals("Paulo", result.get(2).firstName2);
assertEquals("Paulo", result.get(3).firstName2);
assertEquals("1984", result1.get(0).title);
assertEquals("Animal Farm", result1.get(1).title);
assertEquals("O Alquimista", result1.get(2).title);
assertEquals("Brida", result1.get(3).title);
assertEquals("Orwell", result.get(0).lastName);
assertEquals("Orwell", result.get(1).lastName);
assertEquals("Coelho", result.get(2).lastName);
assertEquals("Coelho", result.get(3).lastName);
assertEquals("George", result1.get(0).firstName);
assertEquals("George", result1.get(1).firstName);
assertEquals("Paulo", result1.get(2).firstName);
assertEquals("Paulo", result1.get(3).firstName);
assertEquals("Orwell", result.get(0).lastName2);
assertEquals("Orwell", result.get(1).lastName2);
assertEquals("Coelho", result.get(2).lastName2);
assertEquals("Coelho", result.get(3).lastName2);
assertEquals("George", result1.get(0).firstName2);
assertEquals("George", result1.get(1).firstName2);
assertEquals("Paulo", result1.get(2).firstName2);
assertEquals("Paulo", result1.get(3).firstName2);
assertEquals("Orwell", result1.get(0).lastName);
assertEquals("Orwell", result1.get(1).lastName);
assertEquals("Coelho", result1.get(2).lastName);
assertEquals("Coelho", result1.get(3).lastName);
assertEquals("Orwell", result1.get(0).lastName2);
assertEquals("Orwell", result1.get(1).lastName2);
assertEquals("Coelho", result1.get(2).lastName2);
assertEquals("Coelho", result1.get(3).lastName2);
assertEquals("Orwell", result2.get(0).getLAST_NAME());
assertEquals("Orwell", result2.get(1).getLAST_NAME());
assertEquals("Coelho", result2.get(2).getLAST_NAME());
assertEquals("Coelho", result2.get(3).getLAST_NAME());
try {
// Cannot instanciate an abstract class
@ -447,7 +463,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658,
return;
}
List<BookWithoutAnnotations> result =
Select<?> select =
create().select(
TBook_ID(),
TBook_TITLE(),
@ -456,55 +472,68 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658,
TAuthor_DATE_OF_BIRTH())
.from(TBook())
.join(TAuthor()).on(TBook_AUTHOR_ID().equal(TAuthor_ID()))
.orderBy(TBook_ID())
.fetchInto(BookWithoutAnnotations.class);
.orderBy(TBook_ID());
assertEquals(4, result.size());
List<BookWithoutAnnotations> result1 = select.fetchInto(BookWithoutAnnotations.class);
List<IBookWithoutAnnotations> result2 = select.fetchInto(IBookWithoutAnnotations.class);
assertEquals(1, (int) result.get(0).id);
assertEquals(2, (int) result.get(1).id);
assertEquals(3, (int) result.get(2).id);
assertEquals(4, (int) result.get(3).id);
assertEquals(4, result1.size());
assertEquals(4, result2.size());
assertEquals(1, result.get(0).id2);
assertEquals(2, result.get(1).id2);
assertEquals(3, result.get(2).id2);
assertEquals(4, result.get(3).id2);
assertEquals(1, (int) result1.get(0).id);
assertEquals(2, (int) result1.get(1).id);
assertEquals(3, (int) result1.get(2).id);
assertEquals(4, (int) result1.get(3).id);
assertEquals(1, result.get(0).ID);
assertEquals(2, result.get(1).ID);
assertEquals(3, result.get(2).ID);
assertEquals(4, result.get(3).ID);
assertEquals(1, result1.get(0).id2);
assertEquals(2, result1.get(1).id2);
assertEquals(3, result1.get(2).id2);
assertEquals(4, result1.get(3).id2);
assertEquals("1984", result.get(0).title);
assertEquals("Animal Farm", result.get(1).title);
assertEquals("O Alquimista", result.get(2).title);
assertEquals("Brida", result.get(3).title);
assertEquals(1, result1.get(0).ID);
assertEquals(2, result1.get(1).ID);
assertEquals(3, result1.get(2).ID);
assertEquals(4, result1.get(3).ID);
assertEquals("George", result.get(0).firstName);
assertEquals("George", result.get(1).firstName);
assertEquals("Paulo", result.get(2).firstName);
assertEquals("Paulo", result.get(3).firstName);
assertEquals(1, (int) result2.get(0).getId());
assertEquals(2, (int) result2.get(1).getId());
assertEquals(3, (int) result2.get(2).getId());
assertEquals(4, (int) result2.get(3).getId());
assertEquals("George", result.get(0).firstName2);
assertEquals("George", result.get(1).firstName2);
assertEquals("Paulo", result.get(2).firstName2);
assertEquals("Paulo", result.get(3).firstName2);
assertEquals("1984", result1.get(0).title);
assertEquals("Animal Farm", result1.get(1).title);
assertEquals("O Alquimista", result1.get(2).title);
assertEquals("Brida", result1.get(3).title);
assertEquals("Orwell", result.get(0).lastName);
assertEquals("Orwell", result.get(1).lastName);
assertEquals("Coelho", result.get(2).lastName);
assertEquals("Coelho", result.get(3).lastName);
assertEquals("George", result1.get(0).firstName);
assertEquals("George", result1.get(1).firstName);
assertEquals("Paulo", result1.get(2).firstName);
assertEquals("Paulo", result1.get(3).firstName);
assertEquals("Orwell", result.get(0).lastName2);
assertEquals("Orwell", result.get(1).lastName2);
assertEquals("Coelho", result.get(2).lastName2);
assertEquals("Coelho", result.get(3).lastName2);
assertEquals("George", result1.get(0).firstName2);
assertEquals("George", result1.get(1).firstName2);
assertEquals("Paulo", result1.get(2).firstName2);
assertEquals("Paulo", result1.get(3).firstName2);
assertEquals("Orwell", result.get(0).LAST_NAME);
assertEquals("Orwell", result.get(1).LAST_NAME);
assertEquals("Coelho", result.get(2).LAST_NAME);
assertEquals("Coelho", result.get(3).LAST_NAME);
assertEquals("Orwell", result1.get(0).lastName);
assertEquals("Orwell", result1.get(1).lastName);
assertEquals("Coelho", result1.get(2).lastName);
assertEquals("Coelho", result1.get(3).lastName);
assertEquals("Orwell", result1.get(0).lastName2);
assertEquals("Orwell", result1.get(1).lastName2);
assertEquals("Coelho", result1.get(2).lastName2);
assertEquals("Coelho", result1.get(3).lastName2);
assertEquals("Orwell", result1.get(0).LAST_NAME);
assertEquals("Orwell", result1.get(1).LAST_NAME);
assertEquals("Coelho", result1.get(2).LAST_NAME);
assertEquals("Coelho", result1.get(3).LAST_NAME);
assertEquals("Orwell", result2.get(0).getLAST_NAME());
assertEquals("Orwell", result2.get(1).getLAST_NAME());
assertEquals("Coelho", result2.get(2).getLAST_NAME());
assertEquals("Coelho", result2.get(3).getLAST_NAME());
}
@Test

View File

@ -37,17 +37,20 @@
package org.jooq;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.HashMap;
import javax.persistence.Column;
import org.jooq.exception.DataTypeException;
import org.jooq.exception.MappingException;
import org.jooq.tools.Convert;
import org.jooq.tools.reflect.Reflect;
/**
* A wrapper for database result records returned by
@ -1170,6 +1173,12 @@ public interface Record extends FieldProvider, Store<Object> {
* chosen (as reported by {@link Class#getDeclaredConstructors()}</li>
* <li>When invoking the "matching"
* </ul>
* <h3>If the supplied type is an interface or an abstract class</h3>
* Abstract types are instanciated using Java reflection {@link Proxy}
* mechanisms. The returned proxy will wrap a {@link HashMap} containing
* properties mapped by getters and setters of the supplied type. Methods
* (even JPA-annotated ones) other than standard POJO getters and setters
* are not supported. Details can be seen in {@link Reflect#as(Class)}.
* <h3>Other restrictions</h3>
* <ul>
* <li><code>type</code> must provide a default or a "matching" constructor.

View File

@ -55,6 +55,7 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.jooq.ArrayRecord;
@ -619,9 +620,18 @@ abstract class AbstractRecord extends AbstractStore<Object> implements Record {
// If a default, no argument constructor is present, use that one.
try {
T result;
// [#1470] Return a proxy if the supplied type is an interface
if (Modifier.isAbstract(type.getModifiers())) {
result = Reflect.on(HashMap.class).create().as(type);
}
// [#1340] Allow for using non-public default constructors
T result = Reflect.accessible(type.getDeclaredConstructor()).newInstance();
else {
result = Reflect.accessible(type.getDeclaredConstructor()).newInstance();
}
return intoMutablePOJO(type, result);
}