Author: ppoddar
Date: Wed Feb 11 16:57:26 2009
New Revision: 743396
URL: http://svn.apache.org/viewvc?rev=743396&view=rev
Log:
OPENJPA-900: Consolidate Query Hint related processing. As a byproduct, support
new JPA 2.0 getHins() and getSupportedHints().
Added:
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
Wed Feb 11 16:57:26 2009
@@ -18,11 +18,14 @@
*/
package org.apache.openjpa.jdbc.conf;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import org.apache.openjpa.conf.BrokerFactoryValue;
import org.apache.openjpa.conf.OpenJPAProductDerivation;
import org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory;
+import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.lib.conf.AbstractProductDerivation;
import org.apache.openjpa.lib.conf.ConfigurationProvider;
@@ -48,4 +51,9 @@
}
return false;
}
+
+ @Override
+ public Set<String> getSupportedQueryHints() {
+ return Collections.singleton(OracleDictionary.SELECT_HINT);
+ }
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
Wed Feb 11 16:57:26 2009
@@ -21,13 +21,18 @@
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Reflectable;
import org.apache.openjpa.util.GeneralException;
import org.apache.openjpa.util.UserException;
@@ -754,4 +759,166 @@
public static void set(Object target, Method setter, short value) {
set(target, setter, new Short(value));
}
+
+ /**
+ * Gets all bean-style property names of the given Class or its superclass.
+ * A bean-style property 'abc' exists in Class C iff C has declared
+ * following pair of methods:
+ * public void setAbc(Y y) or public C setAbc(Y y)
+ * public Y getAbc();
+ *
+ * If a getter property is annotated with {...@link Reflectable}, then
+ * it is ignored.
+ *
+ */
+ public static Set<String> getBeanStylePropertyNames(Class c) {
+ if (c == null)
+ return Collections.EMPTY_SET;
+ Method[] methods = c.getMethods();
+ if (methods == null || methods.length < 2)
+ return Collections.EMPTY_SET;
+ Set<String> result = new TreeSet<String>();
+ for (Method m : methods) {
+ if (m.getName().startsWith("get")) {
+ if (!canReflect(m))
+ continue;
+ String prop = StringUtils.capitalize(m.getName()
+ .substring("get".length()));
+ Class rtype = m.getReturnType();
+ try {
+ Method setter = c.getMethod("set"+prop, new Class[]{rtype});
+ if (setter.getReturnType() == void.class ||
+ setter.getReturnType().isAssignableFrom(c))
+ result.add(prop);
+ } catch (NoSuchMethodException e) {
+
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets all public field names of the given Class.
+ *
+ */
+ public static Set<String> getPublicFieldNames(Class c) {
+ if (c == null)
+ return Collections.EMPTY_SET;
+ Field[] fields = c.getFields();
+ if (fields == null || fields.length == 0)
+ return Collections.EMPTY_SET;
+ Set<String> result = new TreeSet<String>();
+ for (Field f : fields) {
+ if (canReflect(f))
+ result.add(f.getName());
+ }
+ return result;
+ }
+
+ /**
+ * Gets values of all field f the given class such that f exactly
+ * match the given modifiers and are of given type (Object implies any
type)
+ * unless f is annotated as {...@link Reflectable}.
+ *
+ */
+ public static <T> Set<T> getFieldValues(Class c, int mods, Class<T> t){
+ if (c == null)
+ return Collections.EMPTY_SET;
+ Field[] fields = c.getFields();
+ if (fields == null || fields.length == 0)
+ return Collections.EMPTY_SET;
+ Set<T> result = new TreeSet<T>();
+ for (Field f : fields) {
+ if (mods == f.getModifiers()
+ && (t == Object.class || t.isAssignableFrom(f.getType()))
+ && canReflect(f)) {
+ try {
+ result.add((T)f.get(null));
+ } catch (IllegalArgumentException e) {
+ } catch (IllegalAccessException e) {
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Affirms if the given member is selected for reflection. The decision is
+ * based on the following truth table on both the class-level and
+ * member-level annotation (null annotation represents MAYBE)
+ *
+ * Class member
+ * MAYBE MAYBE YES
+ * MAYBE YES YES
+ * MAYBE NO NO
+ *
+ * YES MAYBE YES
+ * YES YES YES
+ * YES NO NO
+ *
+ * NO YES YES
+ * NO MAYBE NO
+ * NO NO NO
+ *
+ */
+ static boolean canReflect(Reflectable cls, Reflectable member) {
+ if (cls == null || cls.value()) {
+ return member == null || member.value() == true;
+ } else {
+ return member != null && member.value() == true;
+ }
+ }
+
+ /**
+ * Affirms if the original declaration the given field is annotated
+ * for reflection.
+ */
+ static boolean canReflect(Field field) {
+ Class cls = field.getDeclaringClass();
+ return canReflect((Reflectable)cls.getAnnotation(Reflectable.class),
+ field.getAnnotation(Reflectable.class));
+ }
+
+ /**
+ * Affirms if the original declaration the given method is annotated
+ * for reflection.
+ */
+ static boolean canReflect(Method method) {
+ Class cls = getDeclaringClass(method);
+ if (cls != method.getDeclaringClass())
+ method = getDeclaringMethod(cls, method);
+ return canReflect((Reflectable)cls.getAnnotation(Reflectable.class),
+ method.getAnnotation(Reflectable.class));
+ }
+
+ /**
+ * Gets the declaring class of the given method signature but also checks
+ * if the method is declared in an interface. If yes, then returns the
+ * interface.
+ */
+ public static Class getDeclaringClass(Method m) {
+ if (m == null)
+ return null;
+ Class cls = m.getDeclaringClass();
+ Class[] intfs = cls.getInterfaces();
+ for (Class intf : intfs) {
+ if (getDeclaringMethod(intf, m) != null)
+ cls = intf;
+ }
+ return cls;
+ }
+
+ /**
+ * Gets the method in the given class that has the same signature of the
+ * given method, if exists. Otherwise, null.
+ */
+ public static Method getDeclaringMethod(Class c, Method m) {
+ try {
+ Method m0 = c.getMethod(m.getName(), m.getParameterTypes());
+ return m0;
+ } catch (Exception e) {
+ return null;
+ }
+ }
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
Wed Feb 11 16:57:26 2009
@@ -247,12 +247,23 @@
if (type.isAssignableFrom(o.getClass()))
return o;
- // the only non-numeric conversions we do are to string, or from
+ // the non-numeric conversions we do are to string, or from
// string/char to number, or calendar/date
+ // String to Boolean
+ // String to Integer
boolean num = o instanceof Number;
if (!num) {
if (type == String.class)
return o.toString();
+ else if (type == Boolean.class && o instanceof String)
+ return Boolean.valueOf(o.toString());
+ else if (type == Integer.class && o instanceof String)
+ try {
+ return new Integer(o.toString());
+ } catch (NumberFormatException e) {
+ throw new ClassCastException(_loc.get("cant-convert", o,
+ o.getClass(), type).getMessage());
+ }
else if (type == Character.class) {
String str = o.toString();
if (str != null && str.length() == 1)
Modified:
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
(original)
+++
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
Wed Feb 11 16:57:26 2009
@@ -19,7 +19,9 @@
package org.apache.openjpa.lib.conf;
import java.io.File;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Abstract no-op product derivation for easy extension.
@@ -85,4 +87,8 @@
public void beforeConfigurationClose(Configuration conf) {
}
+
+ public Set<String> getSupportedQueryHints() {
+ return Collections.EMPTY_SET;
+ }
}
Modified:
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
(original)
+++
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
Wed Feb 11 16:57:26 2009
@@ -21,6 +21,7 @@
import java.io.File;
import java.io.IOException;
import java.util.List;
+import java.util.Set;
/**
* Hooks for deriving products with additional functionality.
@@ -157,4 +158,13 @@
* @since 0.9.7
*/
public void beforeConfigurationClose(Configuration conf);
+
+
+ /**
+ * Return set of Query hint keys recognized by this receiver.
+ *
+ * @since 2.0.0
+ */
+ public Set<String> getSupportedQueryHints();
+
}
Modified:
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
(original)
+++
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
Wed Feb 11 16:57:26 2009
@@ -29,6 +29,8 @@
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
+import java.util.Set;
+import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
@@ -434,6 +436,19 @@
collection.add(fqLoc);
}
}
+
+
+ public static Set<String> getSupportedQueryHints() {
+ Set<String> result = new TreeSet<String>();
+ // most specific to least
+ for (int i = _derivations.length - 1; i >= 0; i--) {
+ Set<String> members = _derivations[i].getSupportedQueryHints();
+ if (members != null || !members.isEmpty())
+ result.addAll(members);
+ }
+ return result;
+ }
+
/**
* Compare {...@link ProductDerivation}s.
Added:
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java?rev=743396&view=auto
==============================================================================
---
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
(added)
+++
openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
Wed Feb 11 16:57:26 2009
@@ -0,0 +1,25 @@
+package org.apache.openjpa.lib.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a getter method or field so {...@link Reflection reflection
+ * utility} to control whether the annotated member is recorded during
scanning
+ * for bean-style method or field.
+ *
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ *
+ */
+...@target({TYPE, METHOD, FIELD})
+...@retention(RUNTIME)
+public @interface Reflectable {
+ boolean value() default true;
+}
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java?rev=743396&view=auto
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
(added)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
Wed Feb 11 16:57:26 2009
@@ -0,0 +1,167 @@
+/*
+ * 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.conf;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+import org.apache.openjpa.jdbc.sql.OracleDictionary;
+import org.apache.openjpa.kernel.QueryHints;
+import org.apache.openjpa.persistence.HintHandler;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Tests JPA 2.0 API methods {...@link Query#getSupportedHints()} and
+ * {...@link Query#getHints()}.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class TestQueryHints extends SingleEMFTestCase {
+ EntityManager em;
+ OpenJPAQuery query;
+
+ public void setUp() {
+ super.setUp((Object[])null);
+ em = emf.createEntityManager();
+ String sql = "select * from Person";
+ query = OpenJPAPersistence.cast(em.createNativeQuery(sql));
+ }
+
+ public void testSupportedHintsContainProductDerivationHints() {
+ assertSupportedHint(OracleDictionary.SELECT_HINT, true);
+ }
+
+ public void testSupportedHintsContainFetchPlanHints() {
+ assertSupportedHint("openjpa.FetchPlan.LockTimeout", true);
+ }
+
+ public void testSupportedHintsIgnoresSomeFetchPlanBeanStyleProperty() {
+ assertSupportedHint("openjpa.FetchPlan.QueryResultCache", false);
+ }
+
+ public void testSupportedHintsContainQueryProperty() {
+ assertSupportedHint("openjpa.Subclasses", true);
+ }
+
+ public void testSupportedHintsContainKernelQueryHints() {
+ assertSupportedHint(QueryHints.HINT_IGNORE_PREPARED_QUERY, true);
+ }
+
+ public void testSupportedHintsContainJPAQueryHints() {
+ assertSupportedHint("javax.persistence.query.timeout", true);
+ }
+
+ public void testUnrecognizedKeyIsIgnored() {
+ String unrecognizedKey = "acme.org.hint.SomeThingUnknown";
+ query.setHint(unrecognizedKey, "xyz");
+ assertFalse(query.getHints().containsKey(unrecognizedKey));
+
assertNull(query.getFetchPlan().getDelegate().getHint(unrecognizedKey));
+ }
+
+ public void testRecognizedKeyIsNotRecordedButAvailable() {
+ String recognizedKey = "openjpa.some.derivation.hint";
+ query.setHint(recognizedKey, "abc");
+ assertFalse(query.getHints().containsKey(recognizedKey));
+ assertEquals("abc",
query.getFetchPlan().getDelegate().getHint(recognizedKey));
+ }
+
+ public void testSupportedKeyIsRecordedAndAvailable() {
+ String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
+ query.setHint(supportedKey, 42);
+ assertTrue(query.getHints().containsKey(supportedKey));
+ assertEquals(42, query.getFetchPlan().getFetchBatchSize());
+ }
+
+ public void testSupportedKeyWrongValue() {
+ String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
+ short goodValue = (short)42;
+ float badValue = 57.9f;
+ query.setHint(supportedKey, goodValue);
+ assertTrue(query.getHints().containsKey(supportedKey));
+ assertEquals(goodValue, query.getFetchPlan().getFetchBatchSize());
+
+ try {
+ query.setHint(supportedKey, badValue);
+ fail("Expected to fail to set " + supportedKey + " hint to " +
badValue);
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+
+ public void testSupportedKeyIntegerValueConversion() {
+ String supportedKey = "openjpa.hint.OptimizeResultCount";
+ String goodValue = "57";
+ int badValue = -3;
+ query.setHint(supportedKey, goodValue);
+ assertTrue(query.getHints().containsKey(supportedKey));
+ assertEquals(57,
query.getFetchPlan().getDelegate().getHint(supportedKey));
+
+ try {
+ query.setHint(supportedKey, badValue);
+ fail("Expected to fail to set " + supportedKey + " hint to " +
badValue);
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+
+ public void testSupportedKeyBooleanValueConversion() {
+ String supportedKey = QueryHints.HINT_IGNORE_PREPARED_QUERY;
+ String goodValue = "true";
+ query.setHint(supportedKey, goodValue);
+ assertTrue(query.getHints().containsKey(supportedKey));
+ assertEquals(true,
query.getFetchPlan().getDelegate().getHint(supportedKey));
+
+ goodValue = "false";
+ query.setHint(supportedKey, goodValue);
+ assertTrue(query.getHints().containsKey(supportedKey));
+ assertEquals(false,
query.getFetchPlan().getDelegate().getHint(supportedKey));
+ }
+
+ public void testJPAHintSetsFetchPlan() {
+ String jpaKey = "javax.persistence.query.timeout";
+ query.setHint(jpaKey, 5671);
+ assertEquals(5671, query.getFetchPlan().getLockTimeout());
+ }
+
+ public void testParts() {
+ HintHandler.HintKeyComparator test = new
HintHandler.HintKeyComparator();
+ assertEquals(0, test.countDots("a"));
+ assertEquals(1, test.countDots("a.b"));
+ assertEquals(2, test.countDots("a.b.c"));
+ assertEquals(3, test.countDots("a.b.c.d"));
+ assertEquals(4, test.countDots("a.b.c.d."));
+ assertEquals(1, test.countDots("."));
+ assertEquals(0, test.countDots(""));
+ assertEquals(0, test.countDots(null));
+ }
+
+ void assertSupportedHint(String hint, boolean contains) {
+ if (contains)
+ assertTrue("Expeceted suppoerted hint [" + hint + "]",
+ query.getSupportedHints().contains(hint));
+ else
+ assertFalse("Unexpeceted suppoerted hint [" + hint + "]",
+ query.getSupportedHints().contains(hint));
+ }
+
+
+}
Modified:
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
(original)
+++
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
Wed Feb 11 16:57:26 2009
@@ -22,6 +22,7 @@
import javax.persistence.LockModeType;
import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.lib.util.Reflectable;
import org.apache.openjpa.meta.FetchGroup;
/**
@@ -88,6 +89,7 @@
*
* @since 1.0.0
*/
+ @Reflectable(false)
public boolean getQueryResultCacheEnabled();
/**
@@ -102,6 +104,7 @@
/**
* @deprecated use {...@link #getQueryResultCacheEnabled()} instead.
*/
+ @Reflectable(false)
public boolean getQueryResultCache();
/**
@@ -293,5 +296,6 @@
* @deprecated cast to {...@link FetchPlanImpl} instead. This
* method pierces the published-API boundary, as does the SPI cast.
*/
+ @Reflectable(false)
public org.apache.openjpa.kernel.FetchConfiguration getDelegate();
}
Added:
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java?rev=743396&view=auto
==============================================================================
---
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
(added)
+++
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
Wed Feb 11 16:57:26 2009
@@ -0,0 +1,365 @@
+package org.apache.openjpa.persistence;
+
+import static org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
+import static
org.apache.openjpa.kernel.QueryHints.HINT_INVALIDATE_PREPARED_QUERY;
+import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.enhance.Reflection;
+import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.exps.AggregateListener;
+import org.apache.openjpa.kernel.exps.FilterListener;
+import org.apache.openjpa.lib.conf.ProductDerivation;
+import org.apache.openjpa.lib.conf.ProductDerivations;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.StringDistance;
+
+
+/**
+ * Manages query hint keys and handles their values on behalf of a owning
+ * {...@link QueryImpl}. Uses specific knowledge of hint keys declared in
+ * different parts of the system.
+ *
+ * This receiver collects hint keys from different parts of the system. The
+ * keys are implicitly or explicitly declared by several different mechanics.
+ * This receiver sets the values on behalf of a owning {...@link QueryImpl}
+ * based on the its specific knowledge of these keys.
+ *
+ * The hint keys from following sources are collected and handled:
+ *
+ * 1. {...@link org.apache.openjpa.kernel.QueryHints} interface declares hint
keys
+ * as public static final fields. These fields are collected by reflection.
+ * The values are handled by invoking methods on the owning {...@link
QueryImpl}
+ *
+ * 2. Some hint keys are collected from bean-style property names of {...@link
+ * JDBCFetchPlan} by {...@link Reflection#getBeanStylePropertyNames(Class)
+ * reflection} and prefixed with <code>openjpa.FetchPlan</code>.
+ * Their values are used to set the corresponding property of {...@link
+ * FetchPlan} via {...@link #hintToSetter(FetchPlan, String, Object)
reflection}
+ *
+ * 3. Currently defined <code>javax.persistence.*</code> hint keys have
+ * a equivalent counterpart to one of these FetchPlan keys.
+ * The JPA keys are mapped to equivalent FetchPlan hint keys.
+ *
+ * 4. Some keys directly invoke setters or add listeners to the owning
+ * {...@link QueryImpl}. These hint keys are statically declared in
+ * this receiver itself.
+ *
+ * 5. ProductDerivation may introduce their own query hint keys via {...@link
+ * ProductDerivation#getSupportedQueryHints()}. Their values are set in the
+ * {...@link FetchConfiguration#setHint(String, Object)}
+ *
+ * A hint key is classified into one of the following three categories:
+ *
+ * 1. Supported: A key is known to this receiver as collected from different
+ * parts of the system. The value of a supported key is recorded and
+ * available via {...@link #getHints()} method.
+ * 2. Recognized: A key is not known to this receiver but has a prefix which
+ * is known to this receiver. The value of a recognized key is not
recorded
+ * but its value is available via {...@link
FetchConfiguration#getHint(String)}
+ * 3. Unrecognized: A key is neither supported nor recognized. The value of a
+ * unrecognized key is neither recorded nor set anywhere.
+ *
+ * If an incompatible value is supplied for a supported key, a non-fatal
+ * {...@link ArgumentException} is raised.
+ *
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ *
+ * @nojavadoc
+ */
+public class HintHandler {
+ private final QueryImpl owner;
+ private Map<String, Object> _hints;
+ private Set<String> _supportedKeys;
+ private Set<String> _supportedPrefixes;
+
+ static final String PREFIX_JPA = "javax.persistence.";
+ static final String PREFIX_FETCHPLAN = "openjpa.FetchPlan.";
+
+ // These keys are directly handled in {...@link QueryImpl} class.
+ // Declaring a public static final String variable in this class will
+ // make it register as a supported hint key
+ // if you do not want that then annotate as {...@link Reflectable(false)}.
+ public static final String HINT_SUBCLASSES = "openjpa.Subclasses";
+ public static final String HINT_FILTER_LISTENER = "openjpa.FilterListener";
+ public static final String HINT_FILTER_LISTENERS =
+ "openjpa.FilterListeners";
+ public static final String HINT_AGGREGATE_LISTENER =
+ "openjpa.AggregateListener";
+ public static final String HINT_AGGREGATE_LISTENERS =
+ "openjpa.AggregateListeners";
+
+ // JPA Specification 2.0 keys are mapped to equivalent FetchPlan keys
+ public static Map<String,String> _jpaKeys = new TreeMap<String, String>();
+ static {
+ _jpaKeys.put(addPrefix(PREFIX_JPA, "query.timeout"),
+ addPrefix(PREFIX_FETCHPLAN, "LockTimeout"));
+ }
+
+ private static final String DOT = ".";
+ private static final String BLANK = "";
+ private static final Localizer _loc = Localizer.forPackage(
+ HintHandler.class);
+
+ public HintHandler(QueryImpl impl) {
+ owner = impl;
+ }
+
+ /**
+ * Gets all the recorded hint keys and their values.
+ */
+ @SuppressWarnings("unchecked")
+ public Map<String, Object> getHints() {
+ return _hints == null ? Collections.EMPTY_MAP
+ : Collections.unmodifiableMap(_hints);
+ }
+
+ /**
+ * Record a key-value pair only only if the given key is supported.
+ *
+ * @return FALSE if the key is unrecognized.
+ * null (i.e. MAY BE) if the key is recognized, but not supported.
+ * TRUE if the key is supported.
+ */
+ public Boolean record(String hint, Object value) {
+ if (hint == null)
+ return Boolean.FALSE;
+ if (isSupported(hint)) {
+ if (_hints == null)
+ _hints = new TreeMap<String, Object>();
+ _hints.put(hint, value);
+ return Boolean.TRUE;
+ }
+
+ Log log = owner.getDelegate().getBroker().getConfiguration()
+ .getLog(OpenJPAConfiguration.LOG_RUNTIME);
+ String possible = StringDistance.getClosestLevenshteinDistance(hint,
+ getSupportedHints());
+ if (log.isWarnEnabled()) {
+ log.warn(_loc.get("bad-query-hint", hint, possible));
+ }
+ return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
+ }
+
+ /**
+ * Gets all the supported hint keys. The set of supported hint keys is
+ * statically determined by collecting hint keys from the
ProductDerivations
+ * and reflecting upon some of the known classes.
+ */
+ public Set<String> getSupportedHints() {
+ if (_supportedKeys == null) {
+ _supportedKeys = new TreeSet<String>(new HintKeyComparator());
+ _supportedPrefixes = new TreeSet<String>();
+
+ _supportedKeys.addAll(Reflection.getFieldValues(
+ org.apache.openjpa.kernel.QueryHints.class,
+ Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL,
+ String.class));
+
+ _supportedKeys.addAll(addPrefix(PREFIX_FETCHPLAN,
+ Reflection.getBeanStylePropertyNames(
+ owner.getFetchPlan().getClass())));
+
+ _supportedKeys.addAll(_jpaKeys.keySet());
+
+ _supportedKeys.addAll(Reflection.getFieldValues(
+ HintHandler.class,
+ Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL,
+ String.class));
+
+
_supportedKeys.addAll(ProductDerivations.getSupportedQueryHints());
+
+ for (String key : _supportedKeys) {
+ _supportedPrefixes.add(getPrefixOf(key));
+ }
+ }
+ return _supportedKeys;
+ }
+
+ /**
+ * Add a hint key to the set of supported hint keys.
+ */
+ public void addHintKey(String key) {
+ getSupportedHints().add(key);
+ _supportedPrefixes.add(getPrefixOf(key));
+ }
+
+ public Set<String> getKnownPrefixes() {
+ getSupportedHints();
+ return _supportedPrefixes;
+ }
+
+ /**
+ * Affirms the given key matches one of the supported keys.
+ */
+ public boolean isSupported(String key) {
+ return getSupportedHints().contains(key);
+ }
+
+ /**
+ * Affirms the given key has a prefix that matches with any of the
+ * supported prefixes.
+ */
+ public boolean isSupportedPrefix(String key) {
+ return getKnownPrefixes().contains(getPrefixOf(key));
+ }
+
+ static Set<String> addPrefix(String prefix, Set<String> original) {
+ Set<String> result = new TreeSet<String>();
+ String join = prefix.endsWith(DOT) ? BLANK : DOT;
+ for (String o : original)
+ result.add(prefix + join + o);
+ return result;
+ }
+
+ static String addPrefix(String prefix, String original) {
+ String join = prefix.endsWith(DOT) ? BLANK : DOT;
+ return prefix + join + original;
+ }
+
+ public static String removePrefix(String key, String prefix) {
+ if (prefix == null)
+ return key;
+ if (!prefix.endsWith(DOT))
+ prefix = prefix + DOT;
+ if (key != null && key.startsWith(prefix))
+ return key.substring(prefix.length());
+ return key;
+ }
+
+ static String getPrefixOf(String key) {
+ int index = key == null ? -1 : key.indexOf(DOT);
+ return (index != -1) ? key.substring(0,index) : key;
+ }
+
+ boolean isKnownHintPrefix(String key) {
+ String prefix = getPrefixOf(key);
+ return getKnownPrefixes().contains(prefix);
+ }
+
+ public static boolean hasPrefix(String key, String prefix) {
+ if (key == null || prefix == null)
+ return false;
+ if (!prefix.endsWith(DOT))
+ prefix = prefix + DOT;
+ return key.startsWith(prefix);
+ }
+
+ public void setHint(String key, Object value) {
+ Boolean record = record(key, value);
+ FetchConfiguration fetch = owner.getDelegate().getFetchConfiguration();
+ ClassLoader loader = owner.getDelegate().getBroker().getClassLoader();
+ if (record == Boolean.FALSE)
+ return;
+ if (record == null) {
+ fetch.setHint(key, value);
+ return;
+ }
+ try {
+ if (HINT_SUBCLASSES.equals(key)) {
+ if (value instanceof String)
+ value = Boolean.valueOf((String) value);
+ owner.setSubclasses(((Boolean) value).booleanValue());
+ } else if (HINT_FILTER_LISTENER.equals(key))
+ owner.addFilterListener(Filters.hintToFilterListener(value,
+ loader));
+ else if (HINT_FILTER_LISTENERS.equals(key)) {
+ FilterListener[] arr = Filters.hintToFilterListeners(value,
+ loader);
+ for (int i = 0; i < arr.length; i++)
+ owner.addFilterListener(arr[i]);
+ } else if (HINT_AGGREGATE_LISTENER.equals(key))
+ owner.addAggregateListener(Filters.hintToAggregateListener(
+ value, loader));
+ else if (HINT_AGGREGATE_LISTENERS.equals(key)) {
+ AggregateListener[] arr = Filters.hintToAggregateListeners(
+ value, loader);
+ for (int i = 0; i < arr.length; i++)
+ owner.addAggregateListener(arr[i]);
+ } else if (isFetchPlanHint(key)) {
+ hintToSetter(owner.getFetchPlan(),
+ getFetchPlanProperty(key), value);
+ } else if (HINT_RESULT_COUNT.equals(key)) {
+ int v = (Integer)Filters.convert(value, Integer.class);
+ if (v < 0)
+ throw new
ArgumentException(_loc.get("bad-query-hint-value",
+ key, value), null, null, false);
+ fetch.setHint(key, v);
+ } else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
+ fetch.setHint(key, Filters.convert(value, Boolean.class));
+ owner.invalidatePreparedQuery();
+ } else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
+ fetch.setHint(key, Filters.convert(value, Boolean.class));
+ owner.ignorePreparedQuery();
+ } else { // default
+ fetch.setHint(key, value);
+ }
+ return;
+ } catch (IllegalArgumentException iae) {
+ throw new ArgumentException(_loc.get("bad-query-hint-value",
+ key, value), null, null, false);
+ } catch (ClassCastException ce) {
+ throw new ArgumentException(_loc.get("bad-query-hint-value",
+ key, ce.getMessage()), null, null, false);
+ } catch (Exception e) {
+ throw PersistenceExceptions.toPersistenceException(e);
+ }
+ }
+
+ private boolean isFetchPlanHint(String key) {
+ return key.startsWith(PREFIX_FETCHPLAN)
+ || (_jpaKeys.containsKey(key) &&
isFetchPlanHint(_jpaKeys.get(key)));
+ }
+
+ private String getFetchPlanProperty(String key) {
+ if (key.startsWith(PREFIX_FETCHPLAN))
+ return removePrefix(key, PREFIX_FETCHPLAN);
+ else if (_jpaKeys.containsKey(key))
+ return getFetchPlanProperty(_jpaKeys.get(key));
+ else
+ return key;
+ }
+
+ private void hintToSetter(FetchPlan fetchPlan, String k, Object value) {
+ if (fetchPlan == null || k == null)
+ return;
+
+ Method setter = Reflection.findSetter(fetchPlan.getClass(), k, true);
+ Class paramType = setter.getParameterTypes()[0];
+ if (Enum.class.isAssignableFrom(paramType) && value instanceof String)
+ value = Enum.valueOf(paramType, (String) value);
+
+ Filters.hintToSetter(fetchPlan, k, value);
+ }
+
+ public static class HintKeyComparator implements Comparator<String> {
+ public int compare(String s1, String s2) {
+ if (getPrefixOf(s1).equals(getPrefixOf(s2))) {
+ int n1 = countDots(s1);
+ int n2 = countDots(s2);
+ return (n1 == n2) ? s1.compareTo(s2) : (n1 - n2);
+ } else
+ return s1.compareTo(s2);
+ }
+
+ public int countDots(String s) {
+ if (s == null || s.length() == 0)
+ return 0;
+ int index = s.indexOf(DOT);
+ return (index == -1) ? 0 : countDots(s.substring(index+1)) + 1;
+ }
+ }
+}
Modified:
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
(original)
+++
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
Wed Feb 11 16:57:26 2009
@@ -18,8 +18,9 @@
*/
package org.apache.openjpa.persistence;
+import static org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
+
import java.io.Serializable;
-import java.lang.reflect.Method;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -41,15 +42,12 @@
import javax.persistence.TemporalType;
import org.apache.openjpa.conf.OpenJPAConfiguration;
-import org.apache.openjpa.enhance.Reflection;
import org.apache.openjpa.kernel.Broker;
import org.apache.openjpa.kernel.DelegatingQuery;
import org.apache.openjpa.kernel.DelegatingResultList;
import org.apache.openjpa.kernel.FetchConfiguration;
-import org.apache.openjpa.kernel.Filters;
import org.apache.openjpa.kernel.PreparedQuery;
import org.apache.openjpa.kernel.PreparedQueryCache;
-import org.apache.openjpa.kernel.QueryHints;
import org.apache.openjpa.kernel.QueryLanguages;
import org.apache.openjpa.kernel.QueryOperations;
import org.apache.openjpa.kernel.QueryStatistics;
@@ -63,8 +61,6 @@
import org.apache.openjpa.util.RuntimeExceptionTranslator;
import org.apache.openjpa.util.UserException;
-import static org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
-
/**
* Implementation of {...@link Query} interface.
*
@@ -85,6 +81,8 @@
private Map<String, Object> _named;
private Map<Integer, Object> _positional;
private String _id;
+
+ private final HintHandler _hintHandler;
/**
* Constructor; supply factory exception translator and delegate.
@@ -97,6 +95,7 @@
org.apache.openjpa.kernel.Query query) {
_em = em;
_query = new DelegatingQuery(query, ret);
+ _hintHandler = new HintHandler(this);
}
/**
@@ -254,7 +253,7 @@
}
private Object execute() {
- if (_query.getOperation() != QueryOperations.OP_SELECT)
+ if (!isNative() && _query.getOperation() !=
QueryOperations.OP_SELECT)
throw new
InvalidStateException(_loc.get("not-select-query", _query
.getQueryString()), null, null, false);
@@ -351,81 +350,8 @@
public OpenJPAQuery setHint(String key, Object value) {
_em.assertNotCloseInvoked();
- if (key == null)
- return this;
- if (!key.startsWith("openjpa.")) {
- _query.getFetchConfiguration().setHint(key, value);
- return this;
- }
- String k = key.substring("openjpa.".length());
-
- try {
- if ("Subclasses".equals(k)) {
- if (value instanceof String)
- value = Boolean.valueOf((String) value);
- setSubclasses(((Boolean) value).booleanValue());
- } else if ("FilterListener".equals(k))
-
addFilterListener(Filters.hintToFilterListener(value, _query
- .getBroker().getClassLoader()));
- else if ("FilterListeners".equals(k)) {
- FilterListener[] arr =
Filters.hintToFilterListeners(value,
-
_query.getBroker().getClassLoader());
- for (int i = 0; i < arr.length; i++)
- addFilterListener(arr[i]);
- } else if ("AggregateListener".equals(k))
-
addAggregateListener(Filters.hintToAggregateListener(value,
-
_query.getBroker().getClassLoader()));
- else if ("FilterListeners".equals(k)) {
- AggregateListener[] arr =
Filters.hintToAggregateListeners(
- value,
_query.getBroker().getClassLoader());
- for (int i = 0; i < arr.length; i++)
- addAggregateListener(arr[i]);
- } else if (k.startsWith("FetchPlan.")) {
- k = k.substring("FetchPlan.".length());
- hintToSetter(getFetchPlan(), k, value);
- } else if (k.startsWith("hint.")) {
- if ("hint.OptimizeResultCount".equals(k)) {
- if (value instanceof String) {
- try {
- value = new
Integer((String) value);
- } catch (NumberFormatException
nfe) {
- }
- }
- if (!(value instanceof Number)
- || ((Number)
value).intValue() < 0)
- throw new
ArgumentException(_loc.get(
-
"bad-query-hint-value", key, value), null,
- null, false);
- _query.getFetchConfiguration().setHint(key, value);
- } else if
(QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
- (key)) {
- _query.getFetchConfiguration().setHint(key,
(Boolean)value);
- invalidatePreparedQuery();
- } else if (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
- _query.getFetchConfiguration().setHint(key,
(Boolean)value);
- ignorePreparedQuery();
- } else {
- _query.getFetchConfiguration().setHint(key, value);
- }
- } else
- throw new
ArgumentException(_loc.get("bad-query-hint", key),
- null, null, false);
- return this;
- } catch (Exception e) {
- throw PersistenceExceptions.toPersistenceException(e);
- }
- }
-
- private void hintToSetter(FetchPlan fetchPlan, String k, Object value) {
- if (fetchPlan == null || k == null)
- return;
-
- Method setter = Reflection.findSetter(fetchPlan.getClass(), k,
true);
- Class paramType = setter.getParameterTypes()[0];
- if (Enum.class.isAssignableFrom(paramType) && value instanceof
String)
- value = Enum.valueOf(paramType, (String) value);
-
- Filters.hintToSetter(fetchPlan, k, value);
+ _hintHandler.setHint(key, value);
+ return this;
}
public OpenJPAQuery setParameter(int position, Calendar value,
@@ -613,7 +539,7 @@
*/
//TODO: JPA 2.0 Hints that are not set to FetchConfiguration
public Map<String, Object> getHints() {
- return _query.getFetchConfiguration().getHints();
+ return _hintHandler.getHints();
}
public LockModeType getLockMode() {
@@ -622,8 +548,7 @@
}
public Set<String> getSupportedHints() {
- throw new UnsupportedOperationException(
- "JPA 2.0 - Method not yet implemented");
+ return _hintHandler.getSupportedHints();
}
public Query setLockMode(LockModeType lockMode) {
@@ -704,7 +629,7 @@
/**
* Remove this query from PreparedQueryCache.
*/
- private boolean invalidatePreparedQuery() {
+ boolean invalidatePreparedQuery() {
PreparedQueryCache cache = _em.getPreparedQueryCache();
if (cache == null)
return false;
@@ -716,7 +641,7 @@
* Ignores this query from PreparedQueryCache by recreating the original
* query if it has been cached.
*/
- private void ignorePreparedQuery() {
+ void ignorePreparedQuery() {
PreparedQuery cached = _em.getPreparedQuery(_id);
if (cached == null)
return;
Modified:
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
(original)
+++
openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
Wed Feb 11 16:57:26 2009
@@ -70,7 +70,7 @@
mult-results: Query returned multiple results: "{0}".
no-pos-named-params-mix: Cannot mix named and positional parameters in query \
"{0}".
-bad-query-hint: "{0}" is not a recognized query hint.
+bad-query-hint: "{0}" is not a supported query hint. May be you meant "{1}"?
bad-query-hint-value: Invalid value specified for query hint "{0}": {1}
detached: Cannot perform this operation on detached entity "{0}".
removed: Cannot perform this operation on removed entity "{0}".
Modified:
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
==============================================================================
---
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
(original)
+++
openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
Wed Feb 11 16:57:26 2009
@@ -18,7 +18,9 @@
*/
package org.apache.openjpa.slice;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import org.apache.openjpa.conf.OpenJPAProductDerivation;
import org.apache.openjpa.lib.conf.AbstractProductDerivation;
@@ -98,4 +100,8 @@
log.warn(_loc.get("forced-set-config",
prefix+"."+v.getProperty(), forced));
}
+
+ public Set<String> getSupportedQueryHints() {
+ return Collections.singleton(HINT_TARGET);
+ }
}