OPENJPA-900: Reduce reflection in hint processing. Redesign with explicit hint keys.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@899529 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Pinaki Poddar 2010-01-15 05:23:15 +00:00
parent dad4ad1952
commit b5a412fd42
12 changed files with 675 additions and 475 deletions

View File

@ -18,14 +18,18 @@
*/
package org.apache.openjpa.jdbc.conf;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.lib.conf.AbstractProductDerivation;
@ -37,14 +41,8 @@ import org.apache.openjpa.lib.conf.ConfigurationProvider;
public class JDBCProductDerivation extends AbstractProductDerivation
implements OpenJPAProductDerivation {
private static Set<String> supportedQueryHints = new HashSet<String>(2);
static {
supportedQueryHints.add(MySQLDictionary.SELECT_HINT);
supportedQueryHints.add(OracleDictionary.SELECT_HINT);
supportedQueryHints = Collections.unmodifiableSet(supportedQueryHints);
}
public static final String PREFIX = "openjpa.jdbc";
public void putBrokerFactoryAliases(Map m) {
m.put("jdbc", JDBCBrokerFactory.class.getName());
}
@ -62,8 +60,28 @@ public class JDBCProductDerivation extends AbstractProductDerivation
return false;
}
/**
* Hint keys correspond to some (not all) bean-style mutable property name in JDBCFetchConfiguration.
* The fully qualified key is prefixed with <code>openjpa.jdbc</code>.
*/
private static Set<String> _hints = new HashSet<String>();
static {
_hints.add(PREFIX + ".EagerFetchMode");
_hints.add(PREFIX + ".FetchDirection");
_hints.add(PREFIX + ".TransactionIsolation");
_hints.add(PREFIX + ".JoinSyntax");
_hints.add(PREFIX + ".LRSSize");
_hints.add(PREFIX + ".ResultSetType");
_hints.add(PREFIX + ".SubclassFetchMode");
_hints.add(MySQLDictionary.SELECT_HINT);
_hints.add(OracleDictionary.SELECT_HINT);
_hints = Collections.unmodifiableSet(_hints);
}
@Override
public Set<String> getSupportedQueryHints() {
return supportedQueryHints;
return _hints;
}
}

View File

@ -484,25 +484,25 @@ public class DelegatingFetchConfiguration
}
}
public void setHint(String name, Object value, boolean validate) {
public void setHint(String name, Object value, Object original) {
try {
_fetch.setHint(name, value, validate);
_fetch.setHint(name, value, original);
} catch (RuntimeException re) {
throw translate(re);
}
}
public Object getHint(String name) {
public boolean isHintSet(String key) {
try {
return _fetch.getHint(name);
return _fetch.isHintSet(key);
} catch (RuntimeException re) {
throw translate(re);
}
}
public void addHint(String name, Object value) {
public Object getHint(String name) {
try {
_fetch.addHint(name, value);
return _fetch.getHint(name);
} catch (RuntimeException re) {
throw translate(re);
}
@ -515,7 +515,7 @@ public class DelegatingFetchConfiguration
throw translate(re);
}
}
public int requiresFetch(FieldMetaData fmd) {
try {
return _fetch.requiresFetch(fmd);

View File

@ -126,7 +126,8 @@ public class QueryImpl
// remember the list of all the results we have returned so we
// can free their resources when close or closeAll is called
private transient final Collection<ResultList<?>> _resultLists = new ReferenceHashSet(ReferenceHashSet.WEAK);
private transient final Collection<RemoveOnCloseResultList> _resultLists =
new ReferenceHashSet(ReferenceHashSet.WEAK);
/**
* Construct a query managed by the given broker.
@ -1239,7 +1240,7 @@ public class QueryImpl
boolean detach = (_broker.getAutoDetach() &
AutoDetach.DETACH_NONTXREAD) > 0 && !_broker.isActive();
boolean lrs = range.lrs && !ex.isAggregate(q) && !ex.hasGrouping(q);
ResultList res = (!detach && lrs) ? _fc.newResultList(rop)
ResultList<?> res = (!detach && lrs) ? _fc.newResultList(rop)
: new EagerResultList(rop);
res.setUserObject(new Object[]{rop,ex});
_resultLists.add(decorateResultList(res));
@ -1249,7 +1250,7 @@ public class QueryImpl
/**
* Optionally decorate the native result.
*/
protected ResultList decorateResultList(ResultList res) {
protected RemoveOnCloseResultList decorateResultList(ResultList<?> res) {
return new RemoveOnCloseResultList(res);
}
@ -1260,7 +1261,7 @@ public class QueryImpl
if (_packer != null)
return _packer;
Class resultClass = (_resultClass != null) ? _resultClass
Class<?> resultClass = (_resultClass != null) ? _resultClass
: ex.getResultClass(q);
if (resultClass == null)
return null;
@ -1279,7 +1280,7 @@ public class QueryImpl
// into some result class
_packer = new ResultPacker(_class, getAlias(), resultClass);
} else if (resultClass != null) { // projection
Class[] types = ex.getProjectionTypes(q);
Class<?>[] types = ex.getProjectionTypes(q);
_packer = new ResultPacker(types, aliases, resultClass);
}
}
@ -1346,9 +1347,9 @@ public class QueryImpl
public static boolean isAccessPathDirty(Broker broker,
ClassMetaData[] accessMetas) {
Collection persisted = broker.getPersistedTypes();
Collection updated = broker.getUpdatedTypes();
Collection deleted = broker.getDeletedTypes();
Collection<Class<?>> persisted = broker.getPersistedTypes();
Collection<Class<?>> updated = broker.getUpdatedTypes();
Collection<Class<?>> deleted = broker.getDeletedTypes();
if (persisted.isEmpty() && updated.isEmpty() && deleted.isEmpty())
return false;
@ -1358,7 +1359,7 @@ public class QueryImpl
return true;
// compare dirty classes to the access path classes
Class accClass;
Class<?> accClass;
for (int i = 0; i < accessMetas.length; i++) {
if (accessMetas[i] == null)
continue;
@ -1369,14 +1370,14 @@ public class QueryImpl
return true;
// check for dirty subclass
for (Iterator dirty = persisted.iterator(); dirty.hasNext();)
if (accClass.isAssignableFrom((Class) dirty.next()))
for (Iterator<Class<?>> dirty = persisted.iterator(); dirty.hasNext();)
if (accClass.isAssignableFrom(dirty.next()))
return true;
for (Iterator dirty = updated.iterator(); dirty.hasNext();)
if (accClass.isAssignableFrom((Class) dirty.next()))
for (Iterator<Class<?>> dirty = updated.iterator(); dirty.hasNext();)
if (accClass.isAssignableFrom(dirty.next()))
return true;
for (Iterator dirty = deleted.iterator(); dirty.hasNext();)
if (accClass.isAssignableFrom((Class) dirty.next()))
for (Iterator<Class<?>> dirty = deleted.iterator(); dirty.hasNext();)
if (accClass.isAssignableFrom(dirty.next()))
return true;
}
@ -1401,8 +1402,8 @@ public class QueryImpl
assertOpen();
RemoveOnCloseResultList res;
for (Iterator itr = _resultLists.iterator(); itr.hasNext();) {
res = (RemoveOnCloseResultList) itr.next();
for (Iterator<RemoveOnCloseResultList> itr = _resultLists.iterator(); itr.hasNext();) {
res = itr.next();
if (force || res.isProviderOpen())
res.close(false);
}
@ -1470,9 +1471,9 @@ public class QueryImpl
// don't share mutable objects
_fc.copy(q._fc);
if (q._filtListeners != null)
_filtListeners = new HashMap(q._filtListeners);
_filtListeners = new HashMap<String,FilterListener>(q._filtListeners);
if (q._aggListeners != null)
_aggListeners = new HashMap(q._aggListeners);
_aggListeners = new HashMap<String,AggregateListener>(q._aggListeners);
return true;
} finally {
unlock();
@ -1500,7 +1501,7 @@ public class QueryImpl
}
}
public Class[] getProjectionTypes() {
public Class<?>[] getProjectionTypes() {
lock();
try {
return compileForExecutor().getProjectionTypes(_storeQuery);

View File

@ -19,6 +19,8 @@
package org.apache.openjpa.persistence.jdbc;
import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.persistence.OpenJPAEnum;
/**
* Type of fetching to employ.
@ -27,18 +29,20 @@ import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
* @since 0.4.0
* @published
*/
public enum FetchMode {
NONE(EagerFetchModes.EAGER_NONE),
JOIN(EagerFetchModes.EAGER_JOIN),
PARALLEL(EagerFetchModes.EAGER_PARALLEL);
public enum FetchMode implements OpenJPAEnum<FetchMode>{
NONE(EagerFetchModes.EAGER_NONE, "none"),
JOIN(EagerFetchModes.EAGER_JOIN, "join"),
PARALLEL(EagerFetchModes.EAGER_PARALLEL, "parallel");
private final int eagerFetchConstant;
private FetchMode(int value) {
private final String[] _names;
private FetchMode(int value, String... names) {
eagerFetchConstant = value;
_names = names;
}
int toKernelConstant() {
public int toKernelConstant() {
return eagerFetchConstant;
}
@ -57,4 +61,27 @@ public enum FetchMode {
throw new IllegalArgumentException(kernelConstant + "");
}
}
public int convertToKernelConstant(String s) {
return FetchMode.toKernelConstantFromString(s);
}
public int convertToKernelConstant(int i) {
if (i == FetchConfiguration.DEFAULT)
return i;
for (FetchMode mode : FetchMode.values()) {
if (mode.eagerFetchConstant == i)
return i;
}
throw new IllegalArgumentException(i + " is invalid value for FetchMode");
}
public static int toKernelConstantFromString(String s) {
for (FetchMode level : FetchMode.values()) {
for (String name : level._names)
if (name.equalsIgnoreCase(s) || String.valueOf(level.toKernelConstant()).equals(s))
return level.toKernelConstant();
}
throw new IllegalArgumentException(s + " is not a valid name for " + FetchMode.class.getName());
}
}

View File

@ -18,14 +18,21 @@
*/
package org.apache.openjpa.persistence.jdbc;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.LockModeType;
import org.apache.openjpa.jdbc.kernel.DelegatingJDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
import org.apache.openjpa.kernel.DelegatingFetchConfiguration;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.persistence.FetchPlanImpl;
import org.apache.openjpa.persistence.HintValueConverter;
import org.apache.openjpa.persistence.PersistenceExceptions;
/**
@ -40,6 +47,49 @@ public class JDBCFetchPlanImpl
implements JDBCFetchPlan {
private DelegatingJDBCFetchConfiguration _fetch;
static {
registerHint(new String[]{"openjpa.FetchPlan.EagerFetchMode", "openjpa.jdbc.EagerFetchMode"},
new HintValueConverter.StringToInteger(new String[]{"none", "0", "join", "1", "parallel", "2"},
new int[]{EagerFetchModes.EAGER_NONE, EagerFetchModes.EAGER_NONE,
EagerFetchModes.EAGER_JOIN, EagerFetchModes.EAGER_JOIN,
EagerFetchModes.EAGER_PARALLEL,EagerFetchModes.EAGER_PARALLEL}),
new HintValueConverter.EnumToInteger(FetchMode.class,
new int[]{EagerFetchModes.EAGER_NONE, EagerFetchModes.EAGER_JOIN, EagerFetchModes.EAGER_PARALLEL}));
registerHint(new String[]{"openjpa.JoinSyntax", "openjpa.jdbc.JoinSyntax","openjpa.FetchPlan.JoinSyntax"},
new HintValueConverter.EnumToInteger(JoinSyntax.class,
new int[]{JoinSyntaxes.SYNTAX_SQL92, JoinSyntaxes.SYNTAX_TRADITIONAL, JoinSyntaxes.SYNTAX_DATABASE}),
new HintValueConverter.StringToInteger(new String[]{"sql92", "0", "traditional", "1", "database", "2"},
new int[]{JoinSyntaxes.SYNTAX_SQL92, JoinSyntaxes.SYNTAX_SQL92,
JoinSyntaxes.SYNTAX_TRADITIONAL, JoinSyntaxes.SYNTAX_TRADITIONAL,
JoinSyntaxes.SYNTAX_DATABASE, JoinSyntaxes.SYNTAX_DATABASE}));
registerHint(new String[]{"openjpa.FetchDirection", "openjpa.jdbc.FetchDirection",
"openjpa.FetchPlan.FetchDirection"},
new HintValueConverter.EnumToInteger(FetchDirection.class,
new int[]{ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, ResultSet.FETCH_UNKNOWN}),
new HintValueConverter.StringToInteger(new String[]{"forward", String.valueOf(ResultSet.FETCH_FORWARD),
"reverse", String.valueOf(ResultSet.FETCH_REVERSE),
"unknown", String.valueOf(ResultSet.FETCH_UNKNOWN)},
new int[]{ResultSet.FETCH_FORWARD, ResultSet.FETCH_FORWARD,
ResultSet.FETCH_REVERSE, ResultSet.FETCH_REVERSE,
ResultSet.FETCH_UNKNOWN, ResultSet.FETCH_UNKNOWN}));
registerHint(new String[]{"openjpa.FetchPlan.Isolation", "openjpa.jdbc.TransactionIsolation"},
new HintValueConverter.OpenJPAEnumToInteger(IsolationLevel.DEFAULT));
registerHint(new String[]{"openjpa.FetchPlan.LRSSizeAlgorithm", "openjpa.FetchPlan.LRSSize",
"openjpa.jdbc.LRSSize"},
new HintValueConverter.OpenJPAEnumToInteger(LRSSizeAlgorithm.QUERY));
registerHint(new String[]{"openjpa.FetchPlan.ResultSetType", "openjpa.jdbc.ResultSetType"},
new HintValueConverter.OpenJPAEnumToInteger(ResultSetType.FORWARD_ONLY));
registerHint(new String[]{"openjpa.FetchPlan.SubclassFetchMode", "openjpa.jdbc.SubclassFetchMode"},
new HintValueConverter.OpenJPAEnumToInteger(FetchMode.NONE));
// "openjpa.FetchPlan.FetchDirection"
// _hints.add("openjpa.FetchPlan.LockScope");
// _hints.add("openjpa.FetchPlan.LockTimeout");
// _hints.add("openjpa.FetchPlan.MaxFetchDepth");
// _hints.add("openjpa.FetchPlan.QueryTimeout");
// _hints.add("openjpa.FetchPlan.ReadLockMode");
// _hints.add("openjpa.FetchPlan.WriteLockMode");
}
/**
* Constructor; supply delegate.

View File

@ -20,13 +20,16 @@ package org.apache.openjpa.persistence.jdbc;
import java.sql.ResultSet;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.persistence.OpenJPAEnum;
/**
* Type of result set to use.
*
* @since 1.0.0
* @published
*/
public enum ResultSetType {
public enum ResultSetType implements OpenJPAEnum<ResultSetType>{
FORWARD_ONLY(ResultSet.TYPE_FORWARD_ONLY),
SCROLL_INSENSITIVE(ResultSet.TYPE_SCROLL_INSENSITIVE),
SCROLL_SENSITIVE(ResultSet.TYPE_SCROLL_SENSITIVE);
@ -37,7 +40,7 @@ public enum ResultSetType {
resultSetConstant = value;
}
int toKernelConstant() {
public int toKernelConstant() {
return resultSetConstant;
}
@ -56,4 +59,26 @@ public enum ResultSetType {
throw new IllegalArgumentException(kernelConstant + "");
}
}
public int convertToKernelConstant(String s) {
return ResultSetType.toKernelConstantFromString(s);
}
public int convertToKernelConstant(int i) {
if (i == FetchConfiguration.DEFAULT)
return i;
for (ResultSetType level : ResultSetType.values()) {
if (level.resultSetConstant == i)
return i;
}
throw new IllegalArgumentException(i + " is invalid value for ResultSetType");
}
public static int toKernelConstantFromString(String s) {
for (ResultSetType level : ResultSetType.values()) {
if (level.name().equalsIgnoreCase(s) || String.valueOf(level.toKernelConstant()).equals(s))
return level.toKernelConstant();
}
throw new IllegalArgumentException(s + " is not a valid name for " + ResultSetType.class.getName());
}
}

View File

@ -1633,7 +1633,10 @@ public class EntityManagerImpl
LockModeType lock, boolean requiresTxn) {
// handle properties in map first
configureCurrentCacheModes(fetch, properties);
fetch.addHints(properties);
if (properties != null) {
for (Map.Entry<String, Object> entry : properties.entrySet())
fetch.setHint(entry.getKey(), entry.getValue());
}
// override with the specific lockMode, if needed.
if (lock != null && lock != LockModeType.NONE) {
if (requiresTxn) {

View File

@ -1,212 +0,0 @@
/*
* 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.persistence;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.LockModeType;
import org.apache.openjpa.kernel.FetchConfigurationImpl;
import org.apache.openjpa.kernel.AbstractHintHandler;
import org.apache.openjpa.kernel.MixedLockLevels;
import org.apache.openjpa.lib.conf.ProductDerivations;
import org.apache.openjpa.lib.util.Localizer;
/**
* Fetch plan hint handler. Handles openjpa.FetchPlan.*,
* javax.persistence.lock.* and javax.persistence.query.* hints.
*
* @since 2.0.0
* @nojavadoc
*/
@SuppressWarnings("serial")
public class FetchPlanHintHandler extends AbstractHintHandler {
private static final Localizer _loc = Localizer.forPackage(FetchPlanHintHandler.class);
protected static final String PREFIX_FETCHPLAN = PREFIX_OPENJPA + "FetchPlan.";
// Valid defined product derivation prefixes
protected static final Set<String> validProductPrefixes = new HashSet<String>();
// JPA Specification 2.0 keys are mapped to equivalent FetchPlan keys
protected static final Map<String, String> javaxHintsMap = new HashMap<String, String>();
// hints precedent definitions
protected static final Map<String, String[]> precedenceMap = new HashMap<String, String[]>();
static {
// Initialize valid product prefixes from available product derivations.
for (String prefix : ProductDerivations.getConfigurationPrefixes()) {
validProductPrefixes.add(prefix);
}
// Initialize javax.persistence to openjpa.FetchPlan hint mapping.
javaxHintsMap.put(JPAProperties.LOCK_TIMEOUT, PREFIX_FETCHPLAN + "LockTimeout");
javaxHintsMap.put(JPAProperties.LOCK_SCOPE, PREFIX_FETCHPLAN + "LockScope");
javaxHintsMap.put(JPAProperties.QUERY_TIMEOUT, PREFIX_FETCHPLAN + "QueryTimeout");
// Initialize hint precedence order mapping from list.
String[][] precedenceMapList = {
{ JPAProperties.LOCK_TIMEOUT,
PREFIX_FETCHPLAN + "LockTimeout",
PREFIX_OPENJPA + "LockTimeout" },
{ JPAProperties.LOCK_SCOPE,
PREFIX_FETCHPLAN + "LockScope",
PREFIX_OPENJPA + "LockScope" },
{ JPAProperties.QUERY_TIMEOUT,
PREFIX_FETCHPLAN + "QueryTimeout",
PREFIX_OPENJPA + "QueryTimeout" },
{ PREFIX_FETCHPLAN + "Isolation",
PREFIX_JDBC + "TransactionIsolation" },
{ PREFIX_FETCHPLAN + "EagerFetchMode",
PREFIX_JDBC + "EagerFetchMode" },
{ PREFIX_FETCHPLAN + "FetchDirection",
PREFIX_JDBC + "FetchDirection" },
{ PREFIX_FETCHPLAN + "JoinSyntax",
PREFIX_JDBC + "JoinSyntax" },
{ PREFIX_FETCHPLAN + "LRSSizeAlgorithm",
PREFIX_FETCHPLAN + "LRSSize",
PREFIX_JDBC + "LRSSize" },
{ PREFIX_FETCHPLAN + "ResultSetType",
PREFIX_JDBC + "ResultSetType" },
{ PREFIX_FETCHPLAN + "SubclassFetchMode",
PREFIX_JDBC + "SubclassFetchMode" },
{ PREFIX_FETCHPLAN + "ReadLockMode",
PREFIX_OPENJPA + "ReadLockLevel" },
{ PREFIX_FETCHPLAN + "WriteLockMode",
PREFIX_OPENJPA + "WriteLockLevel" },
{ PREFIX_FETCHPLAN + "FetchBatchSize",
PREFIX_OPENJPA + "FetchBatchSize" },
{ PREFIX_FETCHPLAN + "MaxFetchDepth",
PREFIX_OPENJPA + "MaxFetchDepth" }
};
for (String[] list : precedenceMapList) {
for (String hint : list)
precedenceMap.put(hint, list);
}
}
protected FetchPlanImpl _fPlan;
/**
* Constructor; supply delegate.
*/
public FetchPlanHintHandler(FetchPlanImpl fetchPlan) {
super((FetchConfigurationImpl) fetchPlan.getDelegate());
_fPlan = fetchPlan;
}
public boolean setHint(String hintName, Object value, boolean validateThrowException) {
if (!JPAProperties.isValidKey(hintName)
&& !validProductPrefixes.contains(getPrefixOf(hintName)))
return false;
return super.setHint(hintName, value, validateThrowException);
}
protected boolean setHintInternal(String hintName, Object value, boolean validateThrowException) {
boolean valueSet = false;
if (hintName.startsWith(PREFIX_FETCHPLAN)) {
if (hintName.endsWith("LockMode") && !_fConfig.getContext().isActive()) {
_fConfig.setHint(hintName + ".Defer", toLockLevel(value));
valueSet = true;
} else {
valueSet = hintToSetter(_fPlan, hintName, value);
}
} else {
_fConfig.setHint(hintName, value, validateThrowException);
}
return valueSet;
}
protected String hintToKey(String key) {
// transform product derived prefix to openjpa prefix
if (!key.startsWith(PREFIX_OPENJPA)
&& validProductPrefixes.contains(getPrefixOf(key)))
key = PREFIX_OPENJPA + key.substring(key.indexOf('.') + 1);
// transform javax.persistence.* hints to fetch plan hints.
if (javaxHintsMap.containsKey(key))
key = javaxHintsMap.get(key);
return key;
}
protected boolean hasPrecedent(String key) {
boolean hasPrecedent = true;
String[] list = precedenceMap.get(key);
if (list != null) {
for (String hint : list) {
if (hint.equals(key))
break;
// stop if a higher precedence hint has already defined
if (_fConfig.getHint(hint) != null) {
hasPrecedent = false;
break;
}
}
}
return hasPrecedent;
}
protected void handleException(RuntimeException e) {
throw PersistenceExceptions.toPersistenceException(e);
}
private Integer toLockLevel(Object value) {
Object origValue = value;
if (value instanceof String) {
// to accomodate alias name input in relationship with enum values
// e.g. "optimistic-force-increment" ==
// LockModeType.OPTIMISTIC_FORCE_INCREMENT
String strValue = ((String) value).toUpperCase().replace('-', '_');
value = Enum.valueOf(LockModeType.class, strValue);
}
if (value instanceof LockModeType)
value = MixedLockLevelsHelper.toLockLevel((LockModeType) value);
Integer intValue = null;
if (value instanceof Integer)
intValue = (Integer) value;
if (intValue == null
|| (intValue != MixedLockLevels.LOCK_NONE
&& intValue != MixedLockLevels.LOCK_READ
&& intValue != MixedLockLevels.LOCK_OPTIMISTIC
&& intValue != MixedLockLevels.LOCK_WRITE
&& intValue != MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT
&& intValue != MixedLockLevels.LOCK_PESSIMISTIC_READ
&& intValue != MixedLockLevels.LOCK_PESSIMISTIC_WRITE
&& intValue != MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT)
)
throw new IllegalArgumentException(_loc.get("bad-lock-level",
origValue).getMessage());
return intValue;
}
}

View File

@ -0,0 +1,189 @@
/*
* 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.persistence;
import java.util.Arrays;
/**
* Converts a given user-specified value to a target type consumable by the kernel.
* Used by hint processing.
*
* @author Pinaki Poddar
* @since 2.0.0
* @unpublished
*/
public interface HintValueConverter {
/**
* Convert the user-specified value to a kernel consumable value.
*
* @param original the user-specified value
* @return an equivalent value consumable by a kernel construct.
*
* @exception IllegalArgumentException if the given value can not be converted.
*/
Object convert(Object original);
/**
* Affirm if this receiver can convert the value of the given type.
*/
boolean canConvert(Class<?> type);
/**
* Convert the enum value to an enumerated set of constants.
*
* @author Pinaki Poddar
*
*/
static class EnumToInteger implements HintValueConverter {
private Class<? extends Enum<?>> _type;
private Integer[] map;
public EnumToInteger(Class<? extends Enum<?>> enumType, int[] numbers) {
try {
_type = enumType;
Enum<?>[] values = (Enum<?>[])enumType.getMethod("values", null).invoke(null, (Class<?>[])null);
map = new Integer[values.length];
int i = 0;
for (Enum<?> v : values) {
map[v.ordinal()] = numbers[i++];
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Object convert(Object e) {
if (e.getClass() == _type)
return map[((Enum<?>)e).ordinal()];
return e;
}
public boolean canConvert(Class<?> type) {
return Enum.class.isAssignableFrom(type);
}
}
/**
* Converts an OpenJPA specific enum to an equivalent kernel constant.
*
* @author Pinaki Poddar
*
*/
public static class OpenJPAEnumToInteger implements HintValueConverter {
private OpenJPAEnum<?> _prototype;
public OpenJPAEnumToInteger(OpenJPAEnum<?> prototype) {
_prototype = prototype;
}
public Object convert(Object e) {
if (e.getClass() == _prototype.getClass())
return ((OpenJPAEnum<Enum<?>>)e).toKernelConstant();
if (e instanceof String) {
return _prototype.convertToKernelConstant(e.toString());
}
if (e instanceof Integer) {
return _prototype.convertToKernelConstant((Integer)e);
}
return e;
}
public boolean canConvert(Class<?> type) {
return OpenJPAEnum.class.isAssignableFrom(type)
|| type == String.class
|| type == Integer.class
|| type == int.class;
}
}
/**
* Converts a String to an integer.
*
* @author Pinaki Poddar
*
*/
public static class StringToInteger implements HintValueConverter {
private String[] strings;
private Integer[] numbers;
/**
* Construct a converter that will simply translate a numeric string to a integer.
*/
public StringToInteger() {
}
/**
* Construct a converter that will translate any of the given strings to corresponding integer.
* Both arrays must not be null, must not contain null elements and must have the same dimension.
*
* @param strings
* @param numbers
*/
public StringToInteger(String[] strings, int[] numbers) {
if (strings == null || numbers == null || strings.length != numbers.length)
throw new IllegalArgumentException();
this.strings = new String[strings.length];
this.numbers = new Integer[numbers.length];
for (int i = 0; i < strings.length; i++) {
this.strings[i] = strings[i];
this.numbers[i] = numbers[i];
}
}
public Object convert(Object s) {
if (s instanceof String == false)
return s;
String str = s.toString();
if (strings == null) {
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Can not convert " + str + " . Expected a numeric string");
}
}
for (int i = 0; i < strings.length; i++) {
if (strings[i].equalsIgnoreCase(str))
return numbers[i];
}
throw new IllegalArgumentException("Can not convert " + str + " . Valid input is " +
Arrays.toString(strings));
}
public boolean canConvert(Class<?> cls) {
return String.class == cls;
}
}
public static class StringToBoolean implements HintValueConverter {
public Object convert(Object v) {
if (v instanceof String)
return Boolean.valueOf(v.toString());
if (v instanceof Boolean)
return v;
return v;
}
public boolean canConvert(Class<?> cls) {
return String.class == cls || Boolean.class == cls || boolean.class == cls;
}
}
}

View File

@ -20,6 +20,7 @@ package org.apache.openjpa.persistence;
import javax.persistence.LockModeType;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.MixedLockLevels;
/**
@ -29,7 +30,7 @@ import org.apache.openjpa.kernel.MixedLockLevels;
* @author Albert Lee
* @since 2.0.0
*/
public class MixedLockLevelsHelper {
public class MixedLockLevelsHelper implements HintValueConverter {
/**
* Translates javax.persistence LockModeType to internal lock level.
*/
@ -50,6 +51,24 @@ public class MixedLockLevelsHelper {
return MixedLockLevels.LOCK_PESSIMISTIC_WRITE;
return MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT;
}
public static int toLockLevel(int mode) {
switch (mode) {
case MixedLockLevels.LOCK_OPTIMISTIC:
case MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT:
case MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT:
case MixedLockLevels.LOCK_PESSIMISTIC_READ:
case MixedLockLevels.LOCK_PESSIMISTIC_WRITE:
case MixedLockLevels.LOCK_NONE:
case MixedLockLevels.LOCK_READ:
case MixedLockLevels.LOCK_WRITE:
case FetchConfiguration.DEFAULT:
return mode;
default:
throw new IllegalArgumentException("Unknown lock level " + mode);
}
}
/**
* Translates internal lock level to javax.persistence LockModeType.
@ -71,4 +90,30 @@ public class MixedLockLevelsHelper {
return LockModeType.PESSIMISTIC_WRITE;
return LockModeType.PESSIMISTIC_FORCE_INCREMENT;
}
public boolean canConvert(Class<?> type) {
return type == LockModeType.class || type == String.class || type == Integer.class || type == int.class;
}
public Object convert(Object original) {
if (original instanceof LockModeType)
return MixedLockLevelsHelper.toLockLevel((LockModeType)original);
if (original instanceof String) {
try {
int value = Integer.parseInt(original.toString());
return MixedLockLevelsHelper.toLockLevel(value);
} catch (NumberFormatException nfe) {
if ("none".equalsIgnoreCase(original.toString())) {
return MixedLockLevels.LOCK_NONE;
}
return MixedLockLevelsHelper.toLockLevel(
LockModeType.valueOf(original.toString().toUpperCase().replace('-', '_')));
}
}
if (original instanceof Integer) {
return MixedLockLevelsHelper.toLockLevel((Integer)original);
}
throw new IllegalArgumentException("can not convert " + original + " of " + original.getClass());
}
}

View File

@ -29,16 +29,17 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.conf.Compatibility;
@ -48,6 +49,7 @@ import org.apache.openjpa.conf.OpenJPAProductDerivation;
import org.apache.openjpa.conf.Specification;
import org.apache.openjpa.datacache.DataCacheMode;
import org.apache.openjpa.kernel.MixedLockLevels;
import org.apache.openjpa.kernel.QueryHints;
import org.apache.openjpa.lib.conf.AbstractProductDerivation;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.lib.conf.ConfigurationProvider;
@ -94,9 +96,55 @@ public class PersistenceProductDerivation
private HashMap<String, PUNameCollision> _puNameCollisions
= new HashMap<String,PUNameCollision>();
public static final String PREFIX = "javax.persistence";
private static Set<String> _hints = new HashSet<String>();
// Provider name to filter out PUs that don't belong to this derivation.
protected String _providerImplName;
static {
_hints.add("javax.persistence.lock.timeout");
_hints.add("javax.persistence.query.timeout");
_hints.add("openjpa.FetchPlan.ExtendedPathLookup");
_hints.add("openjpa.FetchBatchSize");
_hints.add("openjpa.FetchPlan.FetchBatchSize");
_hints.add("openjpa.MaxFetchDepth");
_hints.add("openjpa.FetchPlan.MaxFetchDepth");
_hints.add("openjpa.LockTimeout");
_hints.add("openjpa.FetchPlan.LockTimeout");
_hints.add("openjpa.QueryTimeout");
_hints.add("openjpa.FetchPlan.QueryTimeout");
_hints.add("openjpa.FlushBeforeQueries");
_hints.add("openjpa.FetchPlan.FlushBeforeQueries");
_hints.add("openjpa.ReadLockLevel");
_hints.add("openjpa.FetchPlan.ReadLockLevel");
_hints.add("openjpa.WriteLockLevel");
_hints.add("openjpa.FetchPlan.WriteLockLevel");
_hints.add("openjpa.FetchPlan.FetchBatchSize");
_hints.add("openjpa.FetchPlan.LockScope");
_hints.add("openjpa.FetchPlan.LockTimeout");
_hints.add("openjpa.FetchPlan.MaxFetchDepth");
_hints.add("openjpa.FetchPlan.QueryTimeout");
_hints.add("openjpa.FetchPlan.ReadLockMode");
_hints.add("openjpa.FetchPlan.WriteLockMode");
_hints.add(QueryHints.HINT_AGGREGATE_LISTENER);
_hints.add(QueryHints.HINT_AGGREGATE_LISTENERS);
_hints.add(QueryHints.HINT_FILTER_LISTENER);
_hints.add(QueryHints.HINT_FILTER_LISTENERS);
_hints.add(QueryHints.HINT_IGNORE_FINDER);
_hints.add(QueryHints.HINT_IGNORE_PREPARED_QUERY);
_hints.add(QueryHints.HINT_INVALIDATE_FINDER);
_hints.add(QueryHints.HINT_INVALIDATE_PREPARED_QUERY);
_hints.add(QueryHints.HINT_PARAM_MARKER_IN_QUERY);
_hints.add(QueryHints.HINT_RECACHE_FINDER);
_hints.add(QueryHints.HINT_RESULT_COUNT);
_hints.add(QueryHints.HINT_SUBCLASSES);
_hints = Collections.unmodifiableSet(_hints);
}
public PersistenceProductDerivation() {
_providerImplName = PersistenceProviderImpl.class.getName();
}
@ -110,7 +158,12 @@ public class PersistenceProductDerivation
@Override
public String getConfigurationPrefix() {
return "javax.persistence";
return PREFIX;
}
@Override
public Set<String> getSupportedQueryHints() {
return _hints;
}
@Override