diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java index 28f2abee3..d906f06ad 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ResultPacker.java @@ -18,6 +18,7 @@ */ package org.apache.openjpa.kernel; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; @@ -108,7 +109,12 @@ public class ResultPacker { private ResultPacker(Class candidate, Class[] types, String[] aliases, Class resultClass) { _aliases = aliases; - if (resultClass.isPrimitive()) { + if (candidate == resultClass || resultClass.isArray()) { + _resultClass = resultClass; + _sets = null; + _put = null; + _constructor = null; + } else if (resultClass.isPrimitive()) { assertConvertable(candidate, types, resultClass); _resultClass = Filters.wrap(resultClass); _sets = null; @@ -170,6 +176,8 @@ public class ResultPacker { * Pack the given object into an instance of the query's result class. */ public Object pack(Object result) { + if (_resultClass == result.getClass()) + return result; // special cases for efficient basic types where we want to avoid // creating an array for call to general pack method below if (_resultClass == Object.class) @@ -208,6 +216,13 @@ public class ResultPacker { } return result; } + if (_resultClass.isArray()) { + Class elementType = _resultClass.getComponentType(); + Object castResult = Array.newInstance(elementType, result.length); + for (int i = 0; i < result.length; i++) + Array.set(castResult, i, elementType.cast(result[i])); + return castResult; + } if (_resultClass == Object.class) return result[0]; if (_resultClass == HashMap.class || _resultClass == Map.class) { diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java index b1c1bbd64..eec3983f2 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/results/TestMultiselect.java @@ -30,6 +30,7 @@ import org.apache.openjpa.persistence.criteria.CriteriaTest; import org.apache.openjpa.persistence.criteria.Person; import org.apache.openjpa.persistence.criteria.Person_; import org.apache.openjpa.persistence.test.AllowFailure; +import org.apache.openjpa.util.UserException; /** @@ -39,7 +40,7 @@ import org.apache.openjpa.persistence.test.AllowFailure; * */ -@AllowFailure(value=true, message="Tests:16 Errors:1 Failure:7") +@AllowFailure(value=false, message="Tests:16 Errors:1 Failure:1") public class TestMultiselect extends CriteriaTest { private static final Class[] CLASSES = {Person.class}; private static boolean initialized = false; @@ -148,6 +149,7 @@ public class TestMultiselect extends CriteriaTest { assertResult(q, String[].class); } + @AllowFailure(message="TupleArray needs special processing at multiselect") public void testTupleArray() { CriteriaQuery q = cb.createQuery(Tuple[].class); Root p = q.from(Person.class); @@ -229,6 +231,8 @@ public class TestMultiselect extends CriteriaTest { assertResult(q, Object[].class, String.class, Integer.class); } + @AllowFailure(message="Mixing constructor with other projections get CriteriaExpressionBuilder.getProjections() " + + "all messed up") public void testSingleObjectMultipleProjectionsAndConstructor() { CriteriaQuery q = cb.createQuery(Object.class); Root p = q.from(Person.class); @@ -255,12 +259,13 @@ public class TestMultiselect extends CriteriaTest { List result = em.createQuery(q).getResultList(); assertFalse(result.isEmpty()); for (Object row : result) { - assertTrue(row.getClass() + " does not match actual result " + resultClass, resultClass.isInstance(row)); + assertTrue(toClass(row) + " does not match actual result " + toString(resultClass), + resultClass.isInstance(row)); if (resultClass.isArray() && arrayElementClasses != null) { for (int i = 0; i < arrayElementClasses.length; i++) { Object element = Array.get(row, i); - assertTrue(i + "-th array element " + arrayElementClasses[i] + " does not match actual result " - + element.getClass(), arrayElementClasses[i].isInstance(element)); + assertTrue(i + "-th array element " + toString(arrayElementClasses[i]) + + " does not match actual result " + toClass(element), arrayElementClasses[i].isInstance(element)); } } } @@ -270,8 +275,16 @@ public class TestMultiselect extends CriteriaTest { try { em.createQuery(q).getResultList(); fail("Expected to fail " + msg); - } catch (PersistenceException e) { + } catch (UserException e) { // this is an expected exception } } + + String toClass(Object o) { + return toString(o.getClass()); + } + + String toString(Class cls) { + return cls.isArray() ? toString(cls.getComponentType())+"[]" : cls.toString(); + } } diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java index 28e86d8c8..b903bf675 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaExpressionBuilder.java @@ -71,7 +71,7 @@ public class CriteriaExpressionBuilder { //exps.operation = QueryOperations.OP_SELECT; //exps.range = null; // Value[] - //exps.resultClass = null; // Class + exps.resultClass = q.getRuntimeResultClass(); exps.parameterTypes = q.getParameterTypes(); return exps; } @@ -240,13 +240,10 @@ public class CriteriaExpressionBuilder { ExpressionFactory factory, CriteriaQueryImpl q, MetamodelImpl model, Map, Value> exp2Vals) { for (Selection s : selections) { - if(s instanceof TupleSelection ) { - exps.resultClass = TupleImpl.class; + if (s instanceof TupleSelection ) { getProjections(exps, ((TupleSelection)s).getSelectionItems(), projections, aliases, clauses, factory, q, model, exp2Vals); - } - else if (s instanceof NewInstanceSelection) { - exps.resultClass = s.getJavaType(); + } else if (s instanceof NewInstanceSelection) { getProjections(exps, ((NewInstanceSelection)s).getSelectionItems(), projections, aliases, clauses, factory, q, model, exp2Vals); } else { diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java index 857258d57..ee750cbe9 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/criteria/CriteriaQueryImpl.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Set; import java.util.Stack; +import javax.persistence.Tuple; import javax.persistence.criteria.AbstractQuery; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; @@ -46,6 +47,7 @@ import org.apache.openjpa.kernel.exps.Context; import org.apache.openjpa.kernel.exps.ExpressionFactory; import org.apache.openjpa.kernel.exps.QueryExpressions; import org.apache.openjpa.kernel.exps.Value; +import org.apache.openjpa.persistence.TupleImpl; import org.apache.openjpa.persistence.meta.MetamodelImpl; import org.apache.openjpa.persistence.meta.Types; @@ -75,6 +77,7 @@ public class CriteriaQueryImpl implements CriteriaQuery, AliasContext { private Boolean _distinct; private SubqueryImpl _delegator; private final Class _resultClass; + private Class _runtimeResultClass; // AliasContext private int aliasCount = 0; @@ -95,6 +98,7 @@ public class CriteriaQueryImpl implements CriteriaQuery, AliasContext { public CriteriaQueryImpl(MetamodelImpl model, Class resultClass) { this._model = model; this._resultClass = resultClass; + _runtimeResultClass = Tuple.class.isAssignableFrom(resultClass) ? TupleImpl.class : resultClass; _aliases = new HashMap, String>(); } @@ -180,6 +184,8 @@ public class CriteriaQueryImpl implements CriteriaQuery, AliasContext { * @return the modified query */ public CriteriaQuery multiselect(Selection... selections) { + if (selections.length > 1 && _resultClass == Object.class) + _runtimeResultClass = Object[].class; return select(selections); } @@ -524,13 +530,15 @@ public class CriteriaQueryImpl implements CriteriaQuery, AliasContext { return ((SubqueryImpl)parent).getDelegate().getRegisteredRootVariable(root); } - public Class getResultType() { - // TODO Auto-generated method stub - return null; + public Class getResultType() { + return _resultClass; + } + + public Class getRuntimeResultClass() { + return _runtimeResultClass; } - public CriteriaQuery multiselect(List> arg0) { - // TODO Auto-generated method stub - return null; + public CriteriaQuery multiselect(List> list) { + return multiselect(list.toArray(new Selection[list.size()])); } }