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);
+ }
}