Author: fancy
Date: Thu Jan 29 17:51:35 2009
New Revision: 738940
URL: http://svn.apache.org/viewvc?rev=738940&view=rev
Log:
OPENJPA-865 JPA2 Query support for collection-valued input parameter in
in-expression
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CollectionParam.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CollectionParam.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CollectionParam.java?rev=738940&view=auto
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CollectionParam.java
(added)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/CollectionParam.java
Thu Jan 29 17:51:35 2009
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.jdbc.kernel.exps;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.Discriminator;
+import org.apache.openjpa.jdbc.sql.SQLBuffer;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.exps.Parameter;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.util.ImplHelper;
+
+/**
+ * A collection-valued input parameter in an in-expression.
+ *
+ * @author Catalina Wei
+ */
+public class CollectionParam
+ extends Const
+ implements Parameter {
+ private static final Localizer _loc = Localizer.forPackage(
+ CollectionParam.class);
+
+ private final String _name;
+ private Class _type = null;
+ private int _idx = -1;
+ private boolean _container = false;
+
+ /**
+ * Constructor. Supply parameter name and type.
+ */
+ public CollectionParam(String name, Class type) {
+ _name = name;
+ setImplicitType(type);
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public String getParameterName() {
+ return getName();
+ }
+
+ public Class getType() {
+ return _type;
+ }
+
+ public void setImplicitType(Class type) {
+ _type = type;
+ _container = (getMetaData() == null || !ImplHelper.isManagedType(
+ getMetaData().getRepository().getConfiguration(), type))
+ && (Collection.class.isAssignableFrom(type)
+ || Map.class.isAssignableFrom(type));
+ }
+
+ public int getIndex() {
+ return _idx;
+ }
+
+ public void setIndex(int idx) {
+ _idx = idx;
+ }
+
+ public Object getValue(Object[] params) {
+ return Filters.convert(params[_idx], getType());
+ }
+
+ public Object getValue(ExpContext ctx, ExpState state) {
+ ParamExpState pstate = (ParamExpState) state;
+ if (pstate.discValue[0] != null)
+ return Arrays.asList(pstate.discValue);
+ else
+ return getValue(ctx.params);
+ }
+
+ public Object getSQLValue(Select sel, ExpContext ctx, ExpState state) {
+ return ((ParamExpState) state).sqlValue;
+ }
+
+ public ExpState initialize(Select sel, ExpContext ctx, int flags) {
+ return new ParamExpState(ctx.params[_idx]);
+ }
+
+ /**
+ * Expression state.
+ */
+ public static class ParamExpState
+ extends ConstExpState {
+
+ public int size = 0;
+ public Object[] sqlValue = null;
+ public int[] otherLength;
+ public ClassMapping[] mapping = null;
+ public Discriminator[] disc = null;
+ public Object discValue[] = null;
+
+ ParamExpState(Object params) {
+ if (params instanceof Collection)
+ size = ((Collection) params).size();
+ sqlValue = new Object[size];
+ otherLength = new int[size];
+ mapping = new ClassMapping[size];
+ disc = new Discriminator[size];
+ discValue = new Object[size];
+ for (int i = 0; i < size; i++) {
+ sqlValue[i] = null;
+ otherLength[i] = 1;
+ mapping[i] = null;
+ disc[i] = null;
+ discValue[i] = null;
+ }
+ }
+ }
+
+ public void calculateValue(Select sel, ExpContext ctx, ExpState state,
+ Val other, ExpState otherState) {
+ super.calculateValue(sel, ctx, state, other, otherState);
+ ParamExpState pstate = (ParamExpState) state;
+ Object value = getValue(ctx.params);
+
+ if (!(value instanceof Collection))
+ throw new IllegalArgumentException(_loc.get(
+ "not-collection-parm", _name).toString());
+
+ if (((Collection) value).isEmpty())
+ throw new IllegalArgumentException(_loc.get(
+ "empty-collection-parm", _name).toString());
+
+ Iterator itr = ((Collection) value).iterator();
+ for (int i = 0; i < pstate.size && itr.hasNext(); i++) {
+ Object val = itr.next();
+ if (other != null && !_container) {
+ pstate.sqlValue[i] = other.toDataStoreValue(sel, ctx,
+ otherState, val);
+ pstate.otherLength[i] = other.length(sel, ctx, otherState);
+ if (other instanceof Type) {
+ pstate.mapping[i] = ctx.store.getConfiguration().
+ getMappingRepositoryInstance().getMapping((Class) val,
+ ctx.store.getContext().getClassLoader(), true);
+ pstate.disc[i] = pstate.mapping[i].getDiscriminator();
+ pstate.discValue[i] = pstate.disc != null ?
+ pstate.disc[i].getValue() : null;
+ }
+ } else if (ImplHelper.isManageable(val)) {
+ ClassMapping mapping = ctx.store.getConfiguration().
+ getMappingRepositoryInstance().getMapping(val.getClass(),
+ ctx.store.getContext().getClassLoader(), true);
+ pstate.sqlValue[i] = mapping.toDataStoreValue(val,
+ mapping.getPrimaryKeyColumns(), ctx.store);
+ pstate.otherLength[i] = mapping.getPrimaryKeyColumns().length;
+ } else
+ pstate.sqlValue[i] = val;
+ }
+ }
+
+ public void appendTo(Select sel, ExpContext ctx, ExpState state,
+ SQLBuffer sql, int index) {
+ ParamExpState pstate = (ParamExpState) state;
+ for (int i = 0; i < pstate.size; i++) {
+ if (pstate.otherLength[i] > 1)
+ sql.appendValue(((Object[]) pstate.sqlValue[i])[index],
+ pstate.getColumn(index));
+ else if (pstate.cols != null)
+ sql.appendValue(pstate.sqlValue[i], pstate.getColumn(index));
+ else if (pstate.discValue[i] != null)
+ sql.appendValue(pstate.discValue[i]);
+ else
+ sql.appendValue(pstate.sqlValue[i], pstate.getColumn(index));
+ }
+ }
+}
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java?rev=738940&r1=738939&r2=738940&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/JDBCExpressionFactory.java
Thu Jan 29 17:51:35 2009
@@ -275,6 +275,10 @@
return new Param(name, type);
}
+ public Parameter newCollectionValuedParameter(String name, Class type) {
+ return new CollectionParam(name, type);
+ }
+
public Value newExtension(FilterListener listener, Value target,
Value arg) {
return new Extension((JDBCFilterListener) listener,
Modified:
openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties?rev=738940&r1=738939&r2=738940&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/exps/localizer.properties
Thu Jan 29 17:51:35 2009
@@ -28,3 +28,6 @@
cant-convert: Attempt to compare incompatible types "{0}" and "{1}".
invalid-unbound-var: Invalid unbound variable "{0}" in query.
no-order-column: Field "{0}" does not have order column defined".
+not-collection-parm: Invalid input parameter "{0}", a collection-valued \
+ input parameter is expected.
+empty-collection-parm: Input parameter "{0}" is empty.
\ No newline at end of file
Added:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java?rev=738940&view=auto
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java
(added)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java
Thu Jan 29 17:51:35 2009
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.kernel.exps;
+
+import org.apache.openjpa.kernel.StoreContext;
+
+/**
+ * Represents a collection valued input parameter.
+ *
+ * @author Catalina Wei
+ */
+class CollectionParam
+ extends Val
+ implements Parameter {
+
+ private String _name = null;
+ private Class _type = null;
+ private int _index = -1;
+
+ /**
+ * Constructor. Provide parameter name and type.
+ */
+ public CollectionParam(String name, Class type) {
+ _name = name;
+ _type = type;
+ }
+
+ public String getParameterName() {
+ return _name;
+ }
+
+ public Class getType() {
+ return _type;
+ }
+
+ public void setImplicitType(Class type) {
+ _type = type;
+ }
+
+ public void setIndex(int index) {
+ _index = index;
+ }
+
+ public Object getValue(Object[] params) {
+ return params[_index];
+ }
+
+ protected Object eval(Object candidate, Object orig,
+ StoreContext ctx, Object[] params) {
+ return getValue(params);
+ }
+}
+
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java?rev=738940&r1=738939&r2=738940&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
Thu Jan 29 17:51:35 2009
@@ -248,6 +248,12 @@
public Parameter newParameter(String name, Class type);
/**
+ * Return a value representing a collection-valued parameter. The
+ * type may be <code>Object</code> if the parameter is not declared.
+ */
+ public Parameter newCollectionValuedParameter(String name, Class type);
+
+ /**
* Return the value of the given extension.
*/
public Value newExtension(FilterListener listener, Value target,
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java?rev=738940&r1=738939&r2=738940&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
Thu Jan 29 17:51:35 2009
@@ -508,6 +508,10 @@
return new Param(name, type);
}
+ public Parameter newCollectionValuedParameter(String name, Class type) {
+ return new CollectionParam(name, type);
+ }
+
public Value newExtension(FilterListener listener, Value target,
Value arg) {
return new Extension(listener, (Val) target, (Val) arg);
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java?rev=738940&r1=738939&r2=738940&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
Thu Jan 29 17:51:35 2009
@@ -827,8 +827,7 @@
return getParameter(node.text, true);
case JJTCOLLECTIONPARAMETER:
- // TODO: support collection valued parameters
- return getParameter(onlyChild(node).text, true);
+ return getCollectionValuedParameter(node);
case JJTOR: // x OR y
return factory.or(getExpression(left(node)),
@@ -1327,6 +1326,47 @@
}
/**
+ * Record the names and order of collection valued input parameters.
+ */
+ private Parameter getCollectionValuedParameter(JPQLNode node) {
+ JPQLNode child = onlyChild(node);
+ String id = child.text;
+ boolean positional = child.id == JJTPOSITIONALINPUTPARAMETER;
+
+ if (parameterTypes == null)
+ parameterTypes = new LinkedMap(6);
+ if (!parameterTypes.containsKey(id))
+ parameterTypes.put(id, TYPE_OBJECT);
+
+ Class type = Object.class;
+ ClassMetaData meta = null;
+ int index;
+ if (positional) {
+ try {
+ // indexes in JPQL are 1-based, as opposed to 0-based in
+ // the core ExpressionFactory
+ index = Integer.parseInt(id) - 1;
+ } catch (NumberFormatException e) {
+ throw parseException(EX_USER, "bad-positional-parameter",
+ new Object[]{ id }, e);
+ }
+
+ if (index < 0)
+ throw parseException(EX_USER, "bad-positional-parameter",
+ new Object[]{ id }, null);
+ } else {
+ // otherwise the index is just the current size of the params
+ index = parameterTypes.indexOf(id);
+ }
+
+ Parameter param = factory.newCollectionValuedParameter(id, type);
+ param.setMetaData(meta);
+ param.setIndex(index);
+
+ return param;
+ }
+
+ /**
* Checks to see if we should evaluate for a NOT expression.
*/
private Expression evalNot(boolean not, Expression exp) {
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java?rev=738940&r1=738939&r2=738940&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java
Thu Jan 29 17:51:35 2009
@@ -79,15 +79,38 @@
List<CompUser> rs = null;
CompUser user = null;
-// TODO: test when support for collection valued parameters is available
-// Collection params = new ArrayList();
-// params.add(FemaleUser.class);
-// params.add(MaleUser.class);
-// query = "SELECT e FROM CompUser e where TYPE(e) = :params";
-// rs = em.createQuery(query).
-// setParameter("params", params).getResultList();
-// user = rs.get(0);
-// assertEquals("the name is not shannon", "Shannon ", user.getName());
+ // test collection-valued input parameters in in-expressions
+ Collection params = new ArrayList(2);
+ params.add(FemaleUser.class);
+ params.add(MaleUser.class);
+
+ Collection params2 = new ArrayList(3);
+ params2.add(36);
+ params2.add(29);
+ params2.add(19);
+ String param3 = "PC";
+
+ query = "SELECT e FROM CompUser e where TYPE(e) in ?1 and e.age in ?2"
+
+ " and e.computerName = ?3";
+ rs = em.createQuery(query).
+ setParameter(1, params).
+ setParameter(2, params2).
+ setParameter(3, param3).getResultList();
+ user = rs.get(0);
+ assertEquals("the name is not shannon", "Shannon ", user.getName());
+
+ query = "SELECT e FROM CompUser e where TYPE(e) in ?1 and e.age in ?2";
+ rs = em.createQuery(query).
+ setParameter(1, params).
+ setParameter(2, params2).getResultList();
+ user = rs.get(0);
+ assertEquals("the name is not shannon", "Shannon ", user.getName());
+
+ query = "SELECT e FROM CompUser e where TYPE(e) in :params";
+ rs = em.createQuery(query).
+ setParameter("params", params).getResultList();
+ user = rs.get(0);
+ assertEquals("the name is not shannon", "Shannon ", user.getName());
query = "SELECT TYPE(e) FROM MaleUser e where TYPE(e) = MaleUser";
rs = em.createQuery(query).getResultList();