mirror of https://github.com/apache/openjpa.git
OPENJPA-957 - Create Fetch*HintHandler(s) for property processing in EntityManager/Query interface methods.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@762161 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
616f8fdbee
commit
718370e1ef
|
@ -19,8 +19,8 @@
|
||||||
package org.apache.openjpa.jdbc.kernel;
|
package org.apache.openjpa.jdbc.kernel;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.ResultSet;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -31,6 +31,7 @@ import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||||
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
|
import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
|
||||||
import org.apache.openjpa.kernel.FetchConfiguration;
|
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||||
import org.apache.openjpa.kernel.FetchConfigurationImpl;
|
import org.apache.openjpa.kernel.FetchConfigurationImpl;
|
||||||
import org.apache.openjpa.kernel.StoreContext;
|
import org.apache.openjpa.kernel.StoreContext;
|
||||||
|
@ -121,6 +122,13 @@ public class JDBCFetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration setEagerFetchMode(int mode) {
|
public JDBCFetchConfiguration setEagerFetchMode(int mode) {
|
||||||
|
if (mode != DEFAULT
|
||||||
|
&& mode != EagerFetchModes.EAGER_NONE
|
||||||
|
&& mode != EagerFetchModes.EAGER_JOIN
|
||||||
|
&& mode != EagerFetchModes.EAGER_PARALLEL)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
_loc.get("bad-fetch-mode", new Integer(mode)).getMessage());
|
||||||
|
|
||||||
if (mode == DEFAULT) {
|
if (mode == DEFAULT) {
|
||||||
JDBCConfiguration conf = getJDBCConfiguration();
|
JDBCConfiguration conf = getJDBCConfiguration();
|
||||||
if (conf != null)
|
if (conf != null)
|
||||||
|
@ -145,6 +153,13 @@ public class JDBCFetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration setSubclassFetchMode(int mode) {
|
public JDBCFetchConfiguration setSubclassFetchMode(int mode) {
|
||||||
|
if (mode != DEFAULT
|
||||||
|
&& mode != EagerFetchModes.EAGER_NONE
|
||||||
|
&& mode != EagerFetchModes.EAGER_JOIN
|
||||||
|
&& mode != EagerFetchModes.EAGER_PARALLEL)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
_loc.get("bad-fetch-mode", new Integer(mode)).getMessage());
|
||||||
|
|
||||||
if (mode == DEFAULT) {
|
if (mode == DEFAULT) {
|
||||||
JDBCConfiguration conf = getJDBCConfiguration();
|
JDBCConfiguration conf = getJDBCConfiguration();
|
||||||
if (conf != null)
|
if (conf != null)
|
||||||
|
@ -160,6 +175,13 @@ public class JDBCFetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration setResultSetType(int type) {
|
public JDBCFetchConfiguration setResultSetType(int type) {
|
||||||
|
if (type != DEFAULT
|
||||||
|
&& type != ResultSet.TYPE_FORWARD_ONLY
|
||||||
|
&& type != ResultSet.TYPE_SCROLL_INSENSITIVE
|
||||||
|
&& type != ResultSet.TYPE_SCROLL_SENSITIVE)
|
||||||
|
throw new IllegalArgumentException(_loc.get("bad-resultset-type",
|
||||||
|
new Integer(type)).getMessage());
|
||||||
|
|
||||||
if (type == DEFAULT) {
|
if (type == DEFAULT) {
|
||||||
JDBCConfiguration conf = getJDBCConfiguration();
|
JDBCConfiguration conf = getJDBCConfiguration();
|
||||||
if (conf != null)
|
if (conf != null)
|
||||||
|
@ -174,6 +196,13 @@ public class JDBCFetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration setFetchDirection(int direction) {
|
public JDBCFetchConfiguration setFetchDirection(int direction) {
|
||||||
|
if (direction != DEFAULT
|
||||||
|
&& direction != ResultSet.FETCH_FORWARD
|
||||||
|
&& direction != ResultSet.FETCH_REVERSE
|
||||||
|
&& direction != ResultSet.FETCH_UNKNOWN)
|
||||||
|
throw new IllegalArgumentException(_loc.get("bad-fetch-direction",
|
||||||
|
new Integer(direction)).getMessage());
|
||||||
|
|
||||||
if (direction == DEFAULT) {
|
if (direction == DEFAULT) {
|
||||||
JDBCConfiguration conf = getJDBCConfiguration();
|
JDBCConfiguration conf = getJDBCConfiguration();
|
||||||
if (conf != null)
|
if (conf != null)
|
||||||
|
@ -188,6 +217,13 @@ public class JDBCFetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration setLRSSize(int size) {
|
public JDBCFetchConfiguration setLRSSize(int size) {
|
||||||
|
if (size != DEFAULT
|
||||||
|
&& size != LRSSizes.SIZE_QUERY
|
||||||
|
&& size != LRSSizes.SIZE_LAST
|
||||||
|
&& size != LRSSizes.SIZE_UNKNOWN)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
_loc.get("bad-lrs-size", new Integer(size)).getMessage());
|
||||||
|
|
||||||
if (size == DEFAULT) {
|
if (size == DEFAULT) {
|
||||||
JDBCConfiguration conf = getJDBCConfiguration();
|
JDBCConfiguration conf = getJDBCConfiguration();
|
||||||
if (conf != null)
|
if (conf != null)
|
||||||
|
@ -202,6 +238,13 @@ public class JDBCFetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public JDBCFetchConfiguration setJoinSyntax(int syntax) {
|
public JDBCFetchConfiguration setJoinSyntax(int syntax) {
|
||||||
|
if (syntax != DEFAULT
|
||||||
|
&& syntax != JoinSyntaxes.SYNTAX_SQL92
|
||||||
|
&& syntax != JoinSyntaxes.SYNTAX_TRADITIONAL
|
||||||
|
&& syntax != JoinSyntaxes.SYNTAX_DATABASE)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
_loc.get("bad-join-syntax", new Integer(syntax)).getMessage());
|
||||||
|
|
||||||
if (syntax == DEFAULT) {
|
if (syntax == DEFAULT) {
|
||||||
JDBCConfiguration conf = getJDBCConfiguration();
|
JDBCConfiguration conf = getJDBCConfiguration();
|
||||||
if (conf != null)
|
if (conf != null)
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.apache.openjpa.jdbc.schema.Schema;
|
||||||
import org.apache.openjpa.jdbc.schema.Sequence;
|
import org.apache.openjpa.jdbc.schema.Sequence;
|
||||||
import org.apache.openjpa.jdbc.schema.Table;
|
import org.apache.openjpa.jdbc.schema.Table;
|
||||||
import org.apache.openjpa.kernel.Filters;
|
import org.apache.openjpa.kernel.Filters;
|
||||||
|
import org.apache.openjpa.kernel.MixedLockLevels;
|
||||||
import org.apache.openjpa.lib.util.Localizer;
|
import org.apache.openjpa.lib.util.Localizer;
|
||||||
import org.apache.openjpa.meta.JavaTypes;
|
import org.apache.openjpa.meta.JavaTypes;
|
||||||
import org.apache.openjpa.util.OpenJPAException;
|
import org.apache.openjpa.util.OpenJPAException;
|
||||||
|
@ -349,6 +350,9 @@ public class DB2Dictionary
|
||||||
else
|
else
|
||||||
isolationLevel = conf.getTransactionIsolationConstant();
|
isolationLevel = conf.getTransactionIsolationConstant();
|
||||||
|
|
||||||
|
if (fetch.getReadLockLevel() >= MixedLockLevels.LOCK_PESSIMISTIC_WRITE)
|
||||||
|
isolationLevel = Connection.TRANSACTION_SERIALIZABLE;
|
||||||
|
|
||||||
if (isForUpdate) {
|
if (isForUpdate) {
|
||||||
switch (db2ServerType) {
|
switch (db2ServerType) {
|
||||||
case db2ISeriesV5R3OrEarlier:
|
case db2ISeriesV5R3OrEarlier:
|
||||||
|
|
|
@ -100,10 +100,8 @@ native-seq-usage: Usage: java org.apache.openjpa.jdbc.kernel.NativeJDBCSeq\n\
|
||||||
\t[-properties/-p <properties file or resource>]\n\
|
\t[-properties/-p <properties file or resource>]\n\
|
||||||
\t[-<property name> <property value>]*
|
\t[-<property name> <property value>]*
|
||||||
bad-level: Invalid isolation level. Valid levels are -1, \
|
bad-level: Invalid isolation level. Valid levels are -1, \
|
||||||
Connection.TRANSACTION_NONE, Connection.TRANSACTION_READ_UNCOMMITTED, \
|
"none"(0), "read-uncommitted"(1), "read-committed"(2), \
|
||||||
Connection.TRANSACTION_READ_COMMITTED, \
|
"repeatable-read"(4) or "serializable"(8). Specified value: {0}.
|
||||||
Connection.TRANSACTION_REPEATABLE_READ, or \
|
|
||||||
Connection.TRANSACTION_SERIALIZABLE. Specified value: {0}.
|
|
||||||
no-nullable-fk: No nullable foreign key found to resolve circular flush\n\
|
no-nullable-fk: No nullable foreign key found to resolve circular flush\n\
|
||||||
dependency. During flush processing, changes to instances, new\n\
|
dependency. During flush processing, changes to instances, new\n\
|
||||||
instances, and deleted instances must be processed in a specific sequence\n\
|
instances, and deleted instances must be processed in a specific sequence\n\
|
||||||
|
@ -143,3 +141,14 @@ finder-add-pattern: Exclusion pattern "{0}" for finder query has invalidated \
|
||||||
optimistic-violation-lock: An optimistic lock violation was detected when \
|
optimistic-violation-lock: An optimistic lock violation was detected when \
|
||||||
locking object instance.
|
locking object instance.
|
||||||
sql-warning: The statement resulted in SQL warning: {0}
|
sql-warning: The statement resulted in SQL warning: {0}
|
||||||
|
bad-fetch-mode: Invalid fetch mode. Valid values are \
|
||||||
|
"none"(0), "join"(1) or "parallel"(2). Specified value: {0}.
|
||||||
|
bad-resultset-type: Invalid result set type. Valid values are \
|
||||||
|
"forward-only"(1003), "scroll-insensitive"(1004) or \
|
||||||
|
"scroll-sensitive"(1005). Specified value: {0}.
|
||||||
|
bad-fetch-direction: Invalid fetch direction. Valid values are \
|
||||||
|
"forward"(1000), "reverse"(1001) or "unknown"(1002). Specified value: {0}.
|
||||||
|
bad-lrs-size: Invalid LRS size. Valid values are \
|
||||||
|
"unknown"(0), "last"(1) or "query"(2). Specified value: {0}.
|
||||||
|
bad-join-syntax: Invalid join syntax. Valid values are \
|
||||||
|
"sql92"(0), "tradition"(1) or "database"(2). Specified value: {0}.
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.kernel;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||||
|
import org.apache.openjpa.enhance.Reflection;
|
||||||
|
import org.apache.openjpa.lib.util.Localizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default hint handler abstract base class.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
* @nojavadoc
|
||||||
|
*/
|
||||||
|
public abstract class AbstractHintHandler implements Serializable {
|
||||||
|
|
||||||
|
private static final Localizer _loc = Localizer
|
||||||
|
.forPackage(AbstractHintHandler.class);
|
||||||
|
|
||||||
|
protected static final String DOT = ".";
|
||||||
|
protected static final String BLANK = "";
|
||||||
|
|
||||||
|
protected static final String PREFIX_OPENJPA = "openjpa.";
|
||||||
|
protected static final String PREFIX_JDBC = PREFIX_OPENJPA + "jdbc.";
|
||||||
|
|
||||||
|
protected FetchConfigurationImpl _fConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor; supply delegate.
|
||||||
|
*/
|
||||||
|
public AbstractHintHandler(FetchConfigurationImpl fConfig) {
|
||||||
|
_fConfig = fConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean setHintInternal(String hintName, Object value,
|
||||||
|
boolean validateThrowException);
|
||||||
|
|
||||||
|
public boolean setHint(String hintName, Object value,
|
||||||
|
boolean validateThrowException) {
|
||||||
|
String key = hintToKey(hintName);
|
||||||
|
boolean valueSet = !hintName.equals(key);
|
||||||
|
if (hasPrecedent(hintName)) {
|
||||||
|
try {
|
||||||
|
valueSet |= setHintInternal(key, value, validateThrowException);
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
if (validateThrowException) {
|
||||||
|
if (rte instanceof IllegalArgumentException)
|
||||||
|
throw rte;
|
||||||
|
else if (rte instanceof ClassCastException)
|
||||||
|
throw new IllegalArgumentException(_loc.get(
|
||||||
|
"bad-hint-value", key, value, rte.getMessage())
|
||||||
|
.getMessage());
|
||||||
|
else
|
||||||
|
handleException(rte);
|
||||||
|
} else
|
||||||
|
_fConfig.getContext().getConfiguration().getLog(
|
||||||
|
OpenJPAConfiguration.LOG_RUNTIME)
|
||||||
|
.warn(
|
||||||
|
_loc.get("bad-hint-value", key, value, rte
|
||||||
|
.getMessage()));
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
valueSet = true;
|
||||||
|
return valueSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String hintToKey(String key) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasPrecedent(String key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleException(RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean hintToSetter(Object target, String k,
|
||||||
|
Object value) {
|
||||||
|
if (target == null || k == null)
|
||||||
|
return false;
|
||||||
|
// remove key prefix as the source of property name
|
||||||
|
k = getSuffixOf(k);
|
||||||
|
Method setter = Reflection.findSetter(target.getClass(), k, true);
|
||||||
|
Class paramType = setter.getParameterTypes()[0];
|
||||||
|
if (Enum.class.isAssignableFrom(paramType) && value instanceof String) {
|
||||||
|
// to accomodate alias name input in relationship with enum values
|
||||||
|
String strValue = ((String) value).toUpperCase().replace('-', '_');
|
||||||
|
value = Enum.valueOf(paramType, strValue);
|
||||||
|
}
|
||||||
|
Filters.hintToSetter(target, k, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String getPrefixOf(String key) {
|
||||||
|
int firstDot = key == null ? -1 : key.indexOf(DOT);
|
||||||
|
return (firstDot != -1) ? key.substring(0, firstDot) : key;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String getSuffixOf(String key) {
|
||||||
|
int lastDot = key == null ? -1 : key.lastIndexOf(DOT);
|
||||||
|
return (lastDot != -1) ? key.substring(lastDot + 1) : key;
|
||||||
|
}
|
||||||
|
}
|
|
@ -475,6 +475,14 @@ public class DelegatingFetchConfiguration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addHint(String name, Object value) {
|
||||||
|
try {
|
||||||
|
_fetch.addHint(name, value);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw translate(re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> getHints() {
|
public Map<String, Object> getHints() {
|
||||||
try {
|
try {
|
||||||
return _fetch.getHints();
|
return _fetch.getHints();
|
||||||
|
|
|
@ -337,6 +337,15 @@ public interface FetchConfiguration
|
||||||
*/
|
*/
|
||||||
public Object getHint (String name);
|
public Object getHint (String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the hint and the associated value to the list.
|
||||||
|
*
|
||||||
|
* @param name the name of the hint
|
||||||
|
* @param value the value of the hint
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public void addHint(String name, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an immutable view of the currently active hints and their values.
|
* Returns an immutable view of the currently active hints and their values.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.kernel;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch configuration hint handler. Handles openjpa.* and openjpa.jdbc.* hints.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
* @nojavadoc
|
||||||
|
*/
|
||||||
|
public class FetchConfigurationHintHandler extends AbstractHintHandler {
|
||||||
|
|
||||||
|
protected static final Map<String, String> hintsMap =
|
||||||
|
new HashMap<String, String>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Initialize hint to property name mapping.
|
||||||
|
hintsMap.put(PREFIX_JDBC + "TransactionIsolation", "Isolation");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor; supply delegate.
|
||||||
|
*/
|
||||||
|
public FetchConfigurationHintHandler(FetchConfigurationImpl fConfig) {
|
||||||
|
super(fConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setHintInternal(String hintName, Object value,
|
||||||
|
boolean validateThrowException) {
|
||||||
|
boolean valueSet = false;
|
||||||
|
String longPrefix = hintName
|
||||||
|
.substring(0, hintName.lastIndexOf(DOT) + 1);
|
||||||
|
if ((longPrefix.equals(PREFIX_JDBC) || longPrefix
|
||||||
|
.equals(PREFIX_OPENJPA))) {
|
||||||
|
valueSet = hintToSetter(_fConfig, hintToPropName(hintName), value);
|
||||||
|
} else {
|
||||||
|
valueSet = true;
|
||||||
|
}
|
||||||
|
return valueSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String hintToPropName(String hintName) {
|
||||||
|
String propName = hintsMap.get(hintName);
|
||||||
|
if (propName == null) {
|
||||||
|
propName = hintName;
|
||||||
|
}
|
||||||
|
return propName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -98,6 +98,7 @@ public class FetchConfigurationImpl
|
||||||
private boolean _load = true;
|
private boolean _load = true;
|
||||||
private int _availableRecursion;
|
private int _availableRecursion;
|
||||||
private int _availableDepth;
|
private int _availableDepth;
|
||||||
|
private FetchConfigurationHintHandler _hintHandler;
|
||||||
|
|
||||||
public FetchConfigurationImpl() {
|
public FetchConfigurationImpl() {
|
||||||
this(null);
|
this(null);
|
||||||
|
@ -106,6 +107,7 @@ public class FetchConfigurationImpl
|
||||||
protected FetchConfigurationImpl(ConfigurationState state) {
|
protected FetchConfigurationImpl(ConfigurationState state) {
|
||||||
_state = (state == null) ? new ConfigurationState() : state;
|
_state = (state == null) ? new ConfigurationState() : state;
|
||||||
_availableDepth = _state.maxFetchDepth;
|
_availableDepth = _state.maxFetchDepth;
|
||||||
|
_hintHandler = new FetchConfigurationHintHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoreContext getContext() {
|
public StoreContext getContext() {
|
||||||
|
@ -239,6 +241,13 @@ public class FetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public FetchConfiguration setFlushBeforeQueries(int flush) {
|
public FetchConfiguration setFlushBeforeQueries(int flush) {
|
||||||
|
if (flush != DEFAULT
|
||||||
|
&& flush != QueryFlushModes.FLUSH_TRUE
|
||||||
|
&& flush != QueryFlushModes.FLUSH_FALSE
|
||||||
|
&& flush != QueryFlushModes.FLUSH_WITH_CONNECTION)
|
||||||
|
throw new IllegalArgumentException(_loc.get(
|
||||||
|
"bad-flush-before-queries", new Integer(flush)).getMessage());
|
||||||
|
|
||||||
if (flush == DEFAULT && _state.ctx != null)
|
if (flush == DEFAULT && _state.ctx != null)
|
||||||
_state.flushQuery = _state.ctx.getConfiguration().
|
_state.flushQuery = _state.ctx.getConfiguration().
|
||||||
getFlushBeforeQueriesConstant();
|
getFlushBeforeQueriesConstant();
|
||||||
|
@ -455,13 +464,15 @@ public class FetchConfigurationImpl
|
||||||
|
|
||||||
|
|
||||||
public int getReadLockLevel() {
|
public int getReadLockLevel() {
|
||||||
String hintKey = "openjpa.FetchPlan.ReadLockLevel";
|
String lockModeKey = "openjpa.FetchPlan.ReadLockMode";
|
||||||
if (getHint(hintKey) != null) {
|
String deferLockModeKey = lockModeKey + ".Defer";
|
||||||
|
Integer value = (Integer)getHint(deferLockModeKey);
|
||||||
|
if (value != null) {
|
||||||
if (isActiveTransaction()) {
|
if (isActiveTransaction()) {
|
||||||
setReadLockLevel((Integer)removeHint(hintKey));
|
removeHint(deferLockModeKey);
|
||||||
} else {
|
setReadLockLevel(value);
|
||||||
return (Integer)getHint(hintKey);
|
} else
|
||||||
}
|
return value;
|
||||||
}
|
}
|
||||||
return _state.readLockLevel;
|
return _state.readLockLevel;
|
||||||
}
|
}
|
||||||
|
@ -470,6 +481,16 @@ public class FetchConfigurationImpl
|
||||||
if (_state.ctx == null)
|
if (_state.ctx == null)
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
if (level != DEFAULT
|
||||||
|
&& level != MixedLockLevels.LOCK_NONE
|
||||||
|
&& level != MixedLockLevels.LOCK_OPTIMISTIC
|
||||||
|
&& level != MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT
|
||||||
|
&& level != MixedLockLevels.LOCK_PESSIMISTIC_READ
|
||||||
|
&& level != MixedLockLevels.LOCK_PESSIMISTIC_WRITE
|
||||||
|
&& level != MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT)
|
||||||
|
throw new IllegalArgumentException(_loc.get(
|
||||||
|
"bad-lock-level", new Integer(level)).getMessage());
|
||||||
|
|
||||||
lock();
|
lock();
|
||||||
try {
|
try {
|
||||||
assertActiveTransaction();
|
assertActiveTransaction();
|
||||||
|
@ -485,13 +506,15 @@ public class FetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getWriteLockLevel() {
|
public int getWriteLockLevel() {
|
||||||
String hintKey = "openjpa.FetchPlan.WriteLockLevel";
|
String lockModeKey = "openjpa.FetchPlan.WriteLockMode";
|
||||||
if (getHint(hintKey) != null) {
|
String deferLockModeKey = lockModeKey + ".Defer";
|
||||||
|
Integer value = (Integer)getHint(deferLockModeKey);
|
||||||
|
if (value != null) {
|
||||||
if (isActiveTransaction()) {
|
if (isActiveTransaction()) {
|
||||||
setReadLockLevel((Integer)removeHint(hintKey));
|
removeHint(deferLockModeKey);
|
||||||
} else {
|
setWriteLockLevel(value);
|
||||||
return (Integer)getHint(hintKey);
|
} else
|
||||||
}
|
return value;
|
||||||
}
|
}
|
||||||
return _state.writeLockLevel;
|
return _state.writeLockLevel;
|
||||||
}
|
}
|
||||||
|
@ -500,6 +523,16 @@ public class FetchConfigurationImpl
|
||||||
if (_state.ctx == null)
|
if (_state.ctx == null)
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
if (level != DEFAULT
|
||||||
|
&& level != MixedLockLevels.LOCK_NONE
|
||||||
|
&& level != MixedLockLevels.LOCK_OPTIMISTIC
|
||||||
|
&& level != MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT
|
||||||
|
&& level != MixedLockLevels.LOCK_PESSIMISTIC_READ
|
||||||
|
&& level != MixedLockLevels.LOCK_PESSIMISTIC_WRITE
|
||||||
|
&& level != MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT)
|
||||||
|
throw new IllegalArgumentException(_loc.get(
|
||||||
|
"bad-lock-level", new Integer(level)).getMessage());
|
||||||
|
|
||||||
lock();
|
lock();
|
||||||
try {
|
try {
|
||||||
assertActiveTransaction();
|
assertActiveTransaction();
|
||||||
|
@ -537,6 +570,16 @@ public class FetchConfigurationImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHint(String name, Object value) {
|
public void setHint(String name, Object value) {
|
||||||
|
setHint(name, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHint(String name, Object value,
|
||||||
|
boolean validThrowException) {
|
||||||
|
if(_hintHandler.setHint(name, value, validThrowException))
|
||||||
|
addHint(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHint(String name, Object value) {
|
||||||
lock();
|
lock();
|
||||||
try {
|
try {
|
||||||
if (_state.hints == null)
|
if (_state.hints == null)
|
||||||
|
|
|
@ -408,4 +408,12 @@ gap-query-param: Parameter {1} for query "{0}" exceeds the number of {2} \
|
||||||
query-execution-error: Failed to execute query "{0}". Check the query syntax \
|
query-execution-error: Failed to execute query "{0}". Check the query syntax \
|
||||||
for correctness. See nested exception for details.
|
for correctness. See nested exception for details.
|
||||||
invalid-timeout: An invalid timeout of {0} milliseconds was ignored. \
|
invalid-timeout: An invalid timeout of {0} milliseconds was ignored. \
|
||||||
Expected a value that is greater than or equal to -1.
|
Expected a value that is greater than or equal to -1.
|
||||||
|
bad-hint-value: "{1}" is not a valid value for hint "{0}" caused by: {2}.
|
||||||
|
bad-flush-before-queries: Invalid flush before queries type. Valid values are \
|
||||||
|
"true"(0), "false"(1) or "with-connection"(2). Specified value: {0}.
|
||||||
|
bad-lock-level: Invalid lock mode/level. Valid values are \
|
||||||
|
"none"(0), "read"(10), "write"(20), "optimistic"(10), \
|
||||||
|
"optimistic-force-increment"(20), "pessimistic-read"(30), \
|
||||||
|
"pessimistic-write"(40) or "pessimistic-force-increment"(50). \
|
||||||
|
Specified value: {0}.
|
||||||
|
|
|
@ -104,8 +104,6 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertAllSQLInOrder(
|
assertAllSQLInOrder(
|
||||||
"INSERT INTO " + empTableName + " .*",
|
|
||||||
"INSERT INTO " + empTableName + " .*",
|
|
||||||
"INSERT INTO " + empTableName + " .*");
|
"INSERT INTO " + empTableName + " .*");
|
||||||
|
|
||||||
// dynamic runtime test to determine wait time.
|
// dynamic runtime test to determine wait time.
|
||||||
|
@ -150,6 +148,8 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
||||||
em.createQuery("delete from " + empTableName).executeUpdate();
|
em.createQuery("delete from " + empTableName).executeUpdate();
|
||||||
|
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
if (em != null && em.isOpen()) {
|
if (em != null && em.isOpen()) {
|
||||||
em.close();
|
em.close();
|
||||||
|
@ -607,9 +607,10 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
||||||
LockModeType expectedlockMode = (LockModeType)args[2];
|
LockModeType expectedlockMode = (LockModeType)args[2];
|
||||||
LockModeType testinglockMode = em.getLockMode(employee);
|
LockModeType testinglockMode = em.getLockMode(employee);
|
||||||
log.trace("test version: expected=" + expectedlockMode
|
log.trace("test version: expected=" + expectedlockMode
|
||||||
+ ", testing=" + em.getLockMode(employee));
|
+ ", testing=" + testinglockMode);
|
||||||
|
|
||||||
assertEquals("", expectedlockMode, testinglockMode);
|
assertEquals("", getCanonical(expectedlockMode),
|
||||||
|
getCanonical(testinglockMode));
|
||||||
break;
|
break;
|
||||||
case ResetException:
|
case ResetException:
|
||||||
thisThread.throwable = null;
|
thisThread.throwable = null;
|
||||||
|
@ -825,6 +826,14 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LockModeType getCanonical(LockModeType lockMode) {
|
||||||
|
if( lockMode == LockModeType.READ )
|
||||||
|
return LockModeType.OPTIMISTIC;
|
||||||
|
if( lockMode == LockModeType.WRITE )
|
||||||
|
return LockModeType.OPTIMISTIC_FORCE_INCREMENT;
|
||||||
|
return lockMode;
|
||||||
|
}
|
||||||
|
|
||||||
private String processException(Act curAction, Throwable t) {
|
private String processException(Act curAction, Throwable t) {
|
||||||
String failStr = "Caught exception: none";
|
String failStr = "Caught exception: none";
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
* 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.lockmgr;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.LockModeType;
|
||||||
|
|
||||||
|
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||||
|
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfigurationImpl;
|
||||||
|
import org.apache.openjpa.jdbc.sql.DB2Dictionary;
|
||||||
|
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
||||||
|
import org.apache.openjpa.persistence.EntityManagerImpl;
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAEntityManager;
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test hints using EntityManager interface.
|
||||||
|
*/
|
||||||
|
public class TestEmLockMode extends SequencedActionsTest {
|
||||||
|
private static String NON_SUPPORTED_OPTIMISTIC_SQL =
|
||||||
|
"SELECT .* FROM LockEmployee .*";
|
||||||
|
private static String NON_SUPPORTED_FOR_UPDATE_SQL =
|
||||||
|
"SELECT .* FROM LockEmployee .* FOR UPDATE.*";
|
||||||
|
private static String VERSION_UPDATE_SQL =
|
||||||
|
"UPDATE LockEmployee SET version .* WHERE .*";
|
||||||
|
private static String DB2_OPTIMISTIC_SQL =
|
||||||
|
"SELECT .* FROM LockEmployee .* WHERE .*";
|
||||||
|
private static String DB2_PESSIMISTIC_RS_SQL =
|
||||||
|
"SELECT .* FROM LockEmployee .* WITH RS USE .*";
|
||||||
|
private static String DB2_PESSIMISTIC_RR_SQL =
|
||||||
|
"SELECT .* FROM LockEmployee .* WITH RR USE .*";
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
setUp(LockEmployee.class, "openjpa.LockManager", "mixed");
|
||||||
|
commonSetUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test em.find(lockmode);
|
||||||
|
*/
|
||||||
|
public void testFindLockModeIsolations() {
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
|
||||||
|
em.getTransaction().begin();
|
||||||
|
|
||||||
|
commonTestFindLockModeIsolations(em, LockModeType.NONE, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 0, null);
|
||||||
|
commonTestFindLockModeIsolations(em, LockModeType.READ, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestFindLockModeIsolations(em, LockModeType.WRITE, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 1,
|
||||||
|
VERSION_UPDATE_SQL);
|
||||||
|
commonTestFindLockModeIsolations(em, LockModeType.OPTIMISTIC, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestFindLockModeIsolations(em,
|
||||||
|
LockModeType.OPTIMISTIC_FORCE_INCREMENT, 1, DB2_OPTIMISTIC_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL, 1, VERSION_UPDATE_SQL);
|
||||||
|
commonTestFindLockModeIsolations(em, LockModeType.PESSIMISTIC_READ, 2,
|
||||||
|
DB2_PESSIMISTIC_RS_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestFindLockModeIsolations(em, LockModeType.PESSIMISTIC_WRITE, 2,
|
||||||
|
DB2_PESSIMISTIC_RR_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestFindLockModeIsolations(em,
|
||||||
|
LockModeType.PESSIMISTIC_FORCE_INCREMENT, 2,
|
||||||
|
DB2_PESSIMISTIC_RR_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
VERSION_UPDATE_SQL);
|
||||||
|
|
||||||
|
em.getTransaction().rollback();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commonTestFindLockModeIsolations(EntityManager em,
|
||||||
|
LockModeType lockMode, int expectedSupportSQLCount,
|
||||||
|
String expectedSupportSQL, int expectedNonSupportSQLCount,
|
||||||
|
String expectedNonSupportSQL, int expectedVersionUpdateCount,
|
||||||
|
String expectedVersionUpdateSQL) {
|
||||||
|
OpenJPAEntityManager oem = (OpenJPAEntityManager) em.getDelegate();
|
||||||
|
JDBCFetchConfigurationImpl fConfig = (JDBCFetchConfigurationImpl)
|
||||||
|
((EntityManagerImpl) oem).getBroker().getFetchConfiguration();
|
||||||
|
DBDictionary dict = ((JDBCConfiguration) ((OpenJPAEntityManagerSPI) oem)
|
||||||
|
.getConfiguration()).getDBDictionaryInstance();
|
||||||
|
|
||||||
|
em.clear();
|
||||||
|
resetSQL();
|
||||||
|
int beforeIsolation = fConfig.getIsolation();
|
||||||
|
em.find(LockEmployee.class, 1, lockMode);
|
||||||
|
if (dict.supportsIsolationForUpdate() &&
|
||||||
|
dict instanceof DB2Dictionary) {
|
||||||
|
assertEquals(expectedSupportSQLCount, getSQLCount());
|
||||||
|
assertAllSQLInOrder(expectedSupportSQL);
|
||||||
|
} else {
|
||||||
|
assertEquals(expectedNonSupportSQLCount, getSQLCount());
|
||||||
|
assertAllSQLInOrder(expectedNonSupportSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetSQL();
|
||||||
|
em.flush();
|
||||||
|
assertEquals(expectedVersionUpdateCount, getSQLCount());
|
||||||
|
if (expectedVersionUpdateSQL != null)
|
||||||
|
assertAllSQLInOrder(expectedVersionUpdateSQL);
|
||||||
|
|
||||||
|
assertEquals(beforeIsolation, fConfig.getIsolation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test em.refresh(lockmode);
|
||||||
|
*/
|
||||||
|
public void testRefreshLockModeIsolations() {
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
|
||||||
|
em.getTransaction().begin();
|
||||||
|
|
||||||
|
commonTestRefreshLockModeIsolations(em, LockModeType.NONE, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 0, null);
|
||||||
|
commonTestRefreshLockModeIsolations(em, LockModeType.READ, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestRefreshLockModeIsolations(em, LockModeType.WRITE, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 1,
|
||||||
|
VERSION_UPDATE_SQL);
|
||||||
|
commonTestRefreshLockModeIsolations(em, LockModeType.OPTIMISTIC, 1,
|
||||||
|
DB2_OPTIMISTIC_SQL, 1, NON_SUPPORTED_OPTIMISTIC_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestRefreshLockModeIsolations(em,
|
||||||
|
LockModeType.OPTIMISTIC_FORCE_INCREMENT, 1, DB2_OPTIMISTIC_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL, 1, VERSION_UPDATE_SQL);
|
||||||
|
commonTestRefreshLockModeIsolations(em, LockModeType.PESSIMISTIC_READ,
|
||||||
|
2, DB2_PESSIMISTIC_RS_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestRefreshLockModeIsolations(em, LockModeType.PESSIMISTIC_WRITE,
|
||||||
|
2, DB2_PESSIMISTIC_RR_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestRefreshLockModeIsolations(em,
|
||||||
|
LockModeType.PESSIMISTIC_FORCE_INCREMENT, 2,
|
||||||
|
DB2_PESSIMISTIC_RR_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
VERSION_UPDATE_SQL);
|
||||||
|
|
||||||
|
em.getTransaction().rollback();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commonTestRefreshLockModeIsolations(EntityManager em,
|
||||||
|
LockModeType lockMode, int expectedSupportSQLCount,
|
||||||
|
String expectedSupportSQL, int expectedNonSupportSQLCount,
|
||||||
|
String expectedNonSupportSQL, int expectedVersionUpdateCount,
|
||||||
|
String expectedVersionUpdateSQL) {
|
||||||
|
OpenJPAEntityManager oem = (OpenJPAEntityManager) em.getDelegate();
|
||||||
|
JDBCFetchConfigurationImpl fConfig = (JDBCFetchConfigurationImpl)
|
||||||
|
((EntityManagerImpl) oem).getBroker().getFetchConfiguration();
|
||||||
|
DBDictionary dict = ((JDBCConfiguration) ((OpenJPAEntityManagerSPI) oem)
|
||||||
|
.getConfiguration()).getDBDictionaryInstance();
|
||||||
|
|
||||||
|
em.clear();
|
||||||
|
LockEmployee employee = em.find(LockEmployee.class, 1);
|
||||||
|
resetSQL();
|
||||||
|
int beforeIsolation = fConfig.getIsolation();
|
||||||
|
em.refresh(employee, lockMode);
|
||||||
|
if (dict.supportsIsolationForUpdate() &&
|
||||||
|
dict instanceof DB2Dictionary) {
|
||||||
|
assertEquals(expectedSupportSQLCount, getSQLCount());
|
||||||
|
assertAllSQLInOrder(expectedSupportSQL);
|
||||||
|
} else {
|
||||||
|
assertEquals(expectedNonSupportSQLCount, getSQLCount());
|
||||||
|
assertAllSQLInOrder(expectedNonSupportSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetSQL();
|
||||||
|
em.flush();
|
||||||
|
assertEquals(expectedVersionUpdateCount, getSQLCount());
|
||||||
|
if (expectedVersionUpdateSQL != null)
|
||||||
|
assertAllSQLInOrder(expectedVersionUpdateSQL);
|
||||||
|
|
||||||
|
assertEquals(beforeIsolation, fConfig.getIsolation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test em.lock(lockmode);
|
||||||
|
*/
|
||||||
|
public void testLockLockModeIsolations() {
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
|
||||||
|
em.getTransaction().begin();
|
||||||
|
|
||||||
|
commonTestLockLockModeIsolations(em, LockModeType.NONE, 0, null, 0,
|
||||||
|
null, 0, null);
|
||||||
|
commonTestLockLockModeIsolations(em, LockModeType.READ, 0, null, 0,
|
||||||
|
null, 1, NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestLockLockModeIsolations(em, LockModeType.WRITE, 0, null, 0,
|
||||||
|
null, 1, VERSION_UPDATE_SQL);
|
||||||
|
commonTestLockLockModeIsolations(em, LockModeType.OPTIMISTIC, 0, null,
|
||||||
|
0, null, 1, NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestLockLockModeIsolations(em,
|
||||||
|
LockModeType.OPTIMISTIC_FORCE_INCREMENT, 0, null, 0, null, 1,
|
||||||
|
VERSION_UPDATE_SQL);
|
||||||
|
commonTestLockLockModeIsolations(em, LockModeType.PESSIMISTIC_READ, 2,
|
||||||
|
DB2_PESSIMISTIC_RS_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestLockLockModeIsolations(em, LockModeType.PESSIMISTIC_WRITE, 2,
|
||||||
|
DB2_PESSIMISTIC_RR_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
NON_SUPPORTED_OPTIMISTIC_SQL);
|
||||||
|
commonTestLockLockModeIsolations(em,
|
||||||
|
LockModeType.PESSIMISTIC_FORCE_INCREMENT, 2,
|
||||||
|
DB2_PESSIMISTIC_RR_SQL, 2, NON_SUPPORTED_FOR_UPDATE_SQL, 1,
|
||||||
|
VERSION_UPDATE_SQL);
|
||||||
|
|
||||||
|
em.getTransaction().rollback();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commonTestLockLockModeIsolations(EntityManager em,
|
||||||
|
LockModeType lockMode, int expectedSupportSQLCount,
|
||||||
|
String expectedSupportSQL, int expectedNonSupportSQLCount,
|
||||||
|
String expectedNonSupportSQL, int expectedVersionUpdateCount,
|
||||||
|
String expectedVersionUpdateSQL) {
|
||||||
|
OpenJPAEntityManager oem = (OpenJPAEntityManager) em.getDelegate();
|
||||||
|
JDBCFetchConfigurationImpl fConfig = (JDBCFetchConfigurationImpl)
|
||||||
|
((EntityManagerImpl) oem).getBroker().getFetchConfiguration();
|
||||||
|
DBDictionary dict = ((JDBCConfiguration) ((OpenJPAEntityManagerSPI) oem)
|
||||||
|
.getConfiguration()).getDBDictionaryInstance();
|
||||||
|
|
||||||
|
em.clear();
|
||||||
|
LockEmployee employee = em.find(LockEmployee.class, 1);
|
||||||
|
resetSQL();
|
||||||
|
int beforeIsolation = fConfig.getIsolation();
|
||||||
|
em.lock(employee, lockMode);
|
||||||
|
if (dict.supportsIsolationForUpdate() &&
|
||||||
|
dict instanceof DB2Dictionary) {
|
||||||
|
assertEquals(expectedSupportSQLCount, getSQLCount());
|
||||||
|
if (expectedSupportSQL != null)
|
||||||
|
assertAllSQLInOrder(expectedSupportSQL);
|
||||||
|
} else {
|
||||||
|
assertEquals(expectedNonSupportSQLCount, getSQLCount());
|
||||||
|
if (expectedNonSupportSQL != null)
|
||||||
|
assertAllSQLInOrder(expectedNonSupportSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetSQL();
|
||||||
|
em.flush();
|
||||||
|
assertEquals(expectedVersionUpdateCount, getSQLCount());
|
||||||
|
if (expectedVersionUpdateSQL != null)
|
||||||
|
assertAllSQLInOrder(expectedVersionUpdateSQL);
|
||||||
|
|
||||||
|
assertEquals(beforeIsolation, fConfig.getIsolation());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* 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.lockmgr;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.OpenJPAEntityManager;
|
||||||
|
import org.apache.openjpa.persistence.jdbc.JDBCFetchPlan;
|
||||||
|
import org.apache.openjpa.persistence.test.AllowFailure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test hints using EntityManager interface.
|
||||||
|
*/
|
||||||
|
public class TestEmLockTimeout extends SequencedActionsTest {
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
setUp(LockEmployee.class
|
||||||
|
, "openjpa.LockManager", "mixed"
|
||||||
|
);
|
||||||
|
commonSetUp();
|
||||||
|
emf.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test setting lock.timeout at the createEntityManagerFactory.
|
||||||
|
*/
|
||||||
|
public void testSetJavaxLockTimeoutAtProviderCreateEmf() {
|
||||||
|
setUp(LockEmployee.class
|
||||||
|
, "openjpa.LockManager", "mixed"
|
||||||
|
, "javax.persistence.lock.timeout", "13"
|
||||||
|
);
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
OpenJPAEntityManager oem = (OpenJPAEntityManager)em.getDelegate();
|
||||||
|
JDBCFetchPlan fPlan = (JDBCFetchPlan) oem.getFetchPlan();
|
||||||
|
|
||||||
|
int lockTmo1 = fPlan.getLockTimeout();
|
||||||
|
assertEquals(13, lockTmo1);
|
||||||
|
|
||||||
|
em.close();
|
||||||
|
emf.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test setting lock.timeout at the createEntityManagerFactory,
|
||||||
|
* with multiple equivalent entries.
|
||||||
|
*/
|
||||||
|
@AllowFailure(message="OPENJPA-??? - Provider.createEntityManagerFactory" +
|
||||||
|
" does not suppport multiple equivalent properties.")
|
||||||
|
public void testSetLockTimeoutsAtProviderCreateEmf() {
|
||||||
|
setUp(LockEmployee.class
|
||||||
|
, "openjpa.LockManager", "mixed"
|
||||||
|
, "openjpa.LockTimeout", 122
|
||||||
|
, "javax.persistence.lock.timeout", "133"
|
||||||
|
);
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
OpenJPAEntityManager oem = (OpenJPAEntityManager)em.getDelegate();
|
||||||
|
JDBCFetchPlan fPlan = (JDBCFetchPlan) oem.getFetchPlan();
|
||||||
|
|
||||||
|
int lockTmo1 = fPlan.getLockTimeout();
|
||||||
|
assertEquals(133, lockTmo1);
|
||||||
|
|
||||||
|
em.close();
|
||||||
|
emf.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test setting lock.timeout at the em.find(), overriding
|
||||||
|
* value set at createEntityManagerFactory and createEm.
|
||||||
|
*/
|
||||||
|
public void testSetJavaxLockTimeoutAtFind() {
|
||||||
|
setUp(LockEmployee.class
|
||||||
|
, "openjpa.LockManager", "mixed"
|
||||||
|
, "javax.persistence.lock.timeout", "13"
|
||||||
|
);
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
|
||||||
|
Map<String,Object> props2 = new HashMap<String,Object>();
|
||||||
|
props2.put("javax.persistence.lock.timeout", 3333);
|
||||||
|
em.find(LockEmployee.class, 1, props2);
|
||||||
|
|
||||||
|
OpenJPAEntityManager oem = (OpenJPAEntityManager)em.getDelegate();
|
||||||
|
JDBCFetchPlan fPlan = (JDBCFetchPlan) oem.getFetchPlan();
|
||||||
|
int lockTmo3 = fPlan.getLockTimeout();
|
||||||
|
assertEquals(13, lockTmo3);
|
||||||
|
|
||||||
|
em.close();
|
||||||
|
emf.close();
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -23,7 +23,7 @@ import javax.persistence.LockModeType;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 em.find(LockMode) behaviors with "mixed" lock manager.
|
* Test JPA 2.0 em.find(LockMode) behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerFindBasicTest extends SequencedActionsTest {
|
public class TestMixedLockManagerFindBasic extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
|
@ -26,7 +26,7 @@ import javax.persistence.TransactionRequiredException;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 em.find(LockMode) exception behaviors with "mixed" lock manager.
|
* Test JPA 2.0 em.find(LockMode) exception behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerFindExceptionTest extends SequencedActionsTest {
|
public class TestMixedLockManagerFindException extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
|
@ -25,7 +25,7 @@ import javax.persistence.LockModeType;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 LockMode type permutation behaviors with "mixed" lock manager.
|
* Test JPA 2.0 LockMode type permutation behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerFindPermutationTest extends SequencedActionsTest {
|
public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
|
@ -23,7 +23,7 @@ import javax.persistence.LockModeType;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 em.lock(LockMode) basic behaviors with "mixed" lock manager.
|
* Test JPA 2.0 em.lock(LockMode) basic behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerLockBasicTest extends SequencedActionsTest {
|
public class TestMixedLockManagerLockBasic extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
|
@ -25,7 +25,7 @@ import javax.persistence.TransactionRequiredException;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 em.lock(LockMode) exception behaviors with "mixed" lock manager.
|
* Test JPA 2.0 em.lock(LockMode) exception behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerLockExceptionTest extends SequencedActionsTest {
|
public class TestMixedLockManagerLockException extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
|
@ -25,7 +25,7 @@ import javax.persistence.LockModeType;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 LockMode type permutation behaviors with "mixed" lock manager.
|
* Test JPA 2.0 LockMode type permutation behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerLockPermutationTest extends SequencedActionsTest {
|
public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
|
@ -23,7 +23,7 @@ import javax.persistence.LockModeType;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 em.refresh(LockMode) basic behaviors with "mixed" lock manager.
|
* Test JPA 2.0 em.refresh(LockMode) basic behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerRefreshBasicTest extends SequencedActionsTest {
|
public class TestMixedLockManagerRefreshBasic extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
|
@ -25,7 +25,7 @@ import javax.persistence.TransactionRequiredException;
|
||||||
* Test JPA 2.0 em.refresh(LockMode) exception behaviors with "mixed"
|
* Test JPA 2.0 em.refresh(LockMode) exception behaviors with "mixed"
|
||||||
* lock manager.
|
* lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerRefreshExceptionTest extends SequencedActionsTest {
|
public class TestMixedLockManagerRefreshException extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
||||||
, "openjpa.LockManager", "mixed"
|
, "openjpa.LockManager", "mixed"
|
||||||
|
@ -33,6 +33,44 @@ public class MixedLockManagerRefreshExceptionTest extends SequencedActionsTest {
|
||||||
commonSetUp();
|
commonSetUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TransactionRequiredException if there is no transaction
|
||||||
|
*/
|
||||||
|
public void testRefreshNoTxReqExceptions() {
|
||||||
|
Object[][] threadMainTxReqTest = {
|
||||||
|
{Act.CreateEm},
|
||||||
|
{Act.Find},
|
||||||
|
{Act.SaveVersion},
|
||||||
|
{Act.TestEmployee, 1, Default_FirstName},
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.NONE },
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.READ },
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.WRITE },
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.OPTIMISTIC },
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.OPTIMISTIC_FORCE_INCREMENT },
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.PESSIMISTIC_READ},
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.PESSIMISTIC_WRITE},
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
|
||||||
|
{Act.Refresh, 1, LockModeType.PESSIMISTIC_FORCE_INCREMENT },
|
||||||
|
{Act.TestException, 0, null },
|
||||||
|
};
|
||||||
|
launchActionSequence("testLockTxReqExceptions()",
|
||||||
|
null, threadMainTxReqTest);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TransactionRequiredException if there is no transaction
|
* TransactionRequiredException if there is no transaction
|
||||||
*/
|
*/
|
||||||
|
@ -43,28 +81,28 @@ public class MixedLockManagerRefreshExceptionTest extends SequencedActionsTest {
|
||||||
{Act.SaveVersion},
|
{Act.SaveVersion},
|
||||||
{Act.TestEmployee, 1, Default_FirstName},
|
{Act.TestEmployee, 1, Default_FirstName},
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.NONE },
|
{Act.RefreshWithLock, 1, LockModeType.NONE },
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.READ },
|
{Act.RefreshWithLock, 1, LockModeType.READ },
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.WRITE },
|
{Act.RefreshWithLock, 1, LockModeType.WRITE },
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.OPTIMISTIC },
|
{Act.RefreshWithLock, 1, LockModeType.OPTIMISTIC },
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.OPTIMISTIC_FORCE_INCREMENT },
|
{Act.RefreshWithLock, 1, LockModeType.OPTIMISTIC_FORCE_INCREMENT },
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.PESSIMISTIC_READ},
|
{Act.RefreshWithLock, 1, LockModeType.PESSIMISTIC_READ},
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.PESSIMISTIC_WRITE},
|
{Act.RefreshWithLock, 1, LockModeType.PESSIMISTIC_WRITE},
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
|
|
||||||
{Act.Refresh, 1, LockModeType.PESSIMISTIC_FORCE_INCREMENT },
|
{Act.RefreshWithLock, 1, LockModeType.PESSIMISTIC_FORCE_INCREMENT },
|
||||||
{Act.TestException, 0, TransactionRequiredException.class },
|
{Act.TestException, 0, TransactionRequiredException.class },
|
||||||
};
|
};
|
||||||
launchActionSequence("testLockTxReqExceptions()",
|
launchActionSequence("testLockTxReqExceptions()",
|
|
@ -25,7 +25,7 @@ import javax.persistence.LockModeType;
|
||||||
/**
|
/**
|
||||||
* Test JPA 2.0 LockMode type permutation behaviors with "mixed" lock manager.
|
* Test JPA 2.0 LockMode type permutation behaviors with "mixed" lock manager.
|
||||||
*/
|
*/
|
||||||
public class MixedLockManagerRefreshPermutationTest
|
public class TestMixedLockManagerRefreshPermutation
|
||||||
extends SequencedActionsTest {
|
extends SequencedActionsTest {
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(LockEmployee.class
|
setUp(LockEmployee.class
|
|
@ -780,7 +780,7 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// expected - setHint(-2000) should cause an IllegalArgumentException
|
// expected - setHint(-2000) should cause an IllegalArgumentException
|
||||||
checkException("testQueryTimeout5()", e,
|
checkException("testQueryTimeout5()", e,
|
||||||
IllegalArgumentException.class, "Invalid value" );
|
IllegalArgumentException.class, "invalid timeout of -2,000");
|
||||||
} finally {
|
} finally {
|
||||||
if ((em != null) && em.isOpen()) {
|
if ((em != null) && em.isOpen()) {
|
||||||
em.close();
|
em.close();
|
||||||
|
|
|
@ -56,7 +56,6 @@ import org.apache.openjpa.kernel.Broker;
|
||||||
import org.apache.openjpa.kernel.DelegatingBroker;
|
import org.apache.openjpa.kernel.DelegatingBroker;
|
||||||
import org.apache.openjpa.kernel.FetchConfiguration;
|
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||||
import org.apache.openjpa.kernel.FindCallbacks;
|
import org.apache.openjpa.kernel.FindCallbacks;
|
||||||
import org.apache.openjpa.kernel.MixedLockLevels;
|
|
||||||
import org.apache.openjpa.kernel.OpCallbacks;
|
import org.apache.openjpa.kernel.OpCallbacks;
|
||||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
import org.apache.openjpa.kernel.PreparedQuery;
|
import org.apache.openjpa.kernel.PreparedQuery;
|
||||||
|
@ -65,16 +64,12 @@ import org.apache.openjpa.kernel.QueryFlushModes;
|
||||||
import org.apache.openjpa.kernel.QueryLanguages;
|
import org.apache.openjpa.kernel.QueryLanguages;
|
||||||
import org.apache.openjpa.kernel.Seq;
|
import org.apache.openjpa.kernel.Seq;
|
||||||
import org.apache.openjpa.kernel.jpql.JPQLParser;
|
import org.apache.openjpa.kernel.jpql.JPQLParser;
|
||||||
import org.apache.openjpa.lib.conf.Configuration;
|
|
||||||
import org.apache.openjpa.lib.conf.IntValue;
|
|
||||||
import org.apache.openjpa.lib.util.Closeable;
|
import org.apache.openjpa.lib.util.Closeable;
|
||||||
import org.apache.openjpa.lib.util.Localizer;
|
import org.apache.openjpa.lib.util.Localizer;
|
||||||
import org.apache.openjpa.meta.ClassMetaData;
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
import org.apache.openjpa.meta.FieldMetaData;
|
import org.apache.openjpa.meta.FieldMetaData;
|
||||||
import org.apache.openjpa.meta.QueryMetaData;
|
import org.apache.openjpa.meta.QueryMetaData;
|
||||||
import org.apache.openjpa.meta.SequenceMetaData;
|
import org.apache.openjpa.meta.SequenceMetaData;
|
||||||
import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
|
|
||||||
import org.apache.openjpa.persistence.query.QueryBuilderImpl;
|
|
||||||
import org.apache.openjpa.util.Exceptions;
|
import org.apache.openjpa.util.Exceptions;
|
||||||
import org.apache.openjpa.util.ImplHelper;
|
import org.apache.openjpa.util.ImplHelper;
|
||||||
import org.apache.openjpa.util.RuntimeExceptionTranslator;
|
import org.apache.openjpa.util.RuntimeExceptionTranslator;
|
||||||
|
@ -469,19 +464,24 @@ public class EntityManagerImpl
|
||||||
return find(cls, oid, mode, null);
|
return find(cls, oid, mode, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> T find(Class<T> cls, Object oid,
|
||||||
|
Map<String, Object> properties){
|
||||||
|
return find(cls, oid, null, properties);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T find(Class<T> cls, Object oid, LockModeType mode,
|
public <T> T find(Class<T> cls, Object oid, LockModeType mode,
|
||||||
Map<String, Object> properties) {
|
Map<String, Object> properties) {
|
||||||
assertNotCloseInvoked();
|
assertNotCloseInvoked();
|
||||||
if (mode != LockModeType.NONE)
|
if (mode != null && mode != LockModeType.NONE)
|
||||||
_broker.assertActiveTransaction();
|
_broker.assertActiveTransaction();
|
||||||
|
|
||||||
boolean fcPushed = pushLockProperties(mode, properties);
|
processLockProperties(pushFetchPlan(), mode, properties);
|
||||||
try {
|
try {
|
||||||
oid = _broker.newObjectId(cls, oid);
|
oid = _broker.newObjectId(cls, oid);
|
||||||
return (T) _broker.find(oid, true, this);
|
return (T) _broker.find(oid, true, this);
|
||||||
} finally {
|
} finally {
|
||||||
popLockProperties(fcPushed);
|
popFetchPlan();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,6 +726,10 @@ public class EntityManagerImpl
|
||||||
refresh(entity, mode, null);
|
refresh(entity, mode, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void refresh(Object entity, Map<String, Object> properties) {
|
||||||
|
refresh(entity, null, properties);
|
||||||
|
}
|
||||||
|
|
||||||
public void refresh(Object entity, LockModeType mode,
|
public void refresh(Object entity, LockModeType mode,
|
||||||
Map<String, Object> properties) {
|
Map<String, Object> properties) {
|
||||||
assertNotCloseInvoked();
|
assertNotCloseInvoked();
|
||||||
|
@ -733,11 +737,11 @@ public class EntityManagerImpl
|
||||||
_broker.assertActiveTransaction();
|
_broker.assertActiveTransaction();
|
||||||
_broker.assertWriteOperation();
|
_broker.assertWriteOperation();
|
||||||
|
|
||||||
boolean fcPushed = pushLockProperties(mode, properties);
|
processLockProperties(pushFetchPlan(), mode, properties);
|
||||||
try {
|
try {
|
||||||
_broker.refresh(entity, this);
|
_broker.refresh(entity, this);
|
||||||
} finally {
|
} finally {
|
||||||
popLockProperties(fcPushed);
|
popFetchPlan();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1096,9 +1100,7 @@ public class EntityManagerImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
public void lock(Object entity, LockModeType mode) {
|
public void lock(Object entity, LockModeType mode) {
|
||||||
assertNotCloseInvoked();
|
lock(entity, mode, -1);
|
||||||
assertValidAttchedEntity(entity);
|
|
||||||
_broker.lock(entity, MixedLockLevelsHelper.toLockLevel(mode), -1, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void lock(Object entity) {
|
public void lock(Object entity) {
|
||||||
|
@ -1110,8 +1112,14 @@ public class EntityManagerImpl
|
||||||
public void lock(Object entity, LockModeType mode, int timeout) {
|
public void lock(Object entity, LockModeType mode, int timeout) {
|
||||||
assertNotCloseInvoked();
|
assertNotCloseInvoked();
|
||||||
assertValidAttchedEntity(entity);
|
assertValidAttchedEntity(entity);
|
||||||
_broker.lock(entity, MixedLockLevelsHelper.toLockLevel(mode), timeout,
|
|
||||||
this);
|
processLockProperties(pushFetchPlan(), mode, null);
|
||||||
|
try {
|
||||||
|
_broker.lock(entity, MixedLockLevelsHelper.toLockLevel(mode),
|
||||||
|
timeout, this);
|
||||||
|
} finally {
|
||||||
|
popFetchPlan();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void lock(Object entity, LockModeType mode,
|
public void lock(Object entity, LockModeType mode,
|
||||||
|
@ -1120,12 +1128,12 @@ public class EntityManagerImpl
|
||||||
assertValidAttchedEntity(entity);
|
assertValidAttchedEntity(entity);
|
||||||
_broker.assertActiveTransaction();
|
_broker.assertActiveTransaction();
|
||||||
|
|
||||||
boolean fcPushed = pushLockProperties(mode, properties);
|
processLockProperties(pushFetchPlan(), mode, properties);
|
||||||
try {
|
try {
|
||||||
_broker.lock(entity, MixedLockLevelsHelper.toLockLevel(mode),
|
_broker.lock(entity, MixedLockLevelsHelper.toLockLevel(mode),
|
||||||
_broker.getFetchConfiguration().getLockTimeout(), this);
|
_broker.getFetchConfiguration().getLockTimeout(), this);
|
||||||
} finally {
|
} finally {
|
||||||
popLockProperties(fcPushed);
|
popFetchPlan();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,7 +1551,6 @@ public class EntityManagerImpl
|
||||||
.toString(), null, this, false);
|
.toString(), null, this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setQuerySQLCache(boolean flag) {
|
public void setQuerySQLCache(boolean flag) {
|
||||||
_broker.setCachePreparedQuery(flag);
|
_broker.setCachePreparedQuery(flag);
|
||||||
}
|
}
|
||||||
|
@ -1556,154 +1563,26 @@ public class EntityManagerImpl
|
||||||
return _ret;
|
return _ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum FetchConfigProperty {
|
private void processLockProperties(FetchPlan fPlan, LockModeType mode,
|
||||||
LockTimeout, ReadLockLevel, WriteLockLevel
|
|
||||||
};
|
|
||||||
|
|
||||||
private boolean setFetchConfigProperty(FetchConfigProperty[] validProps,
|
|
||||||
Map<String, Object> properties) {
|
Map<String, Object> properties) {
|
||||||
boolean fcPushed = false;
|
|
||||||
if (properties != null && properties.size() > 0) {
|
|
||||||
Configuration conf = _broker.getConfiguration();
|
|
||||||
Set<String> inKeys = properties.keySet();
|
|
||||||
for (String inKey : inKeys) {
|
|
||||||
for (FetchConfigProperty validProp : validProps) {
|
|
||||||
String validPropStr = validProp.toString();
|
|
||||||
Set<String> validPropKeys = conf
|
|
||||||
.getPropertyKeys(validPropStr);
|
|
||||||
|
|
||||||
if (validPropKeys.contains(inKey)) {
|
|
||||||
FetchConfiguration fCfg = _broker
|
|
||||||
.getFetchConfiguration();
|
|
||||||
IntValue intVal = new IntValue(inKey);
|
|
||||||
try {
|
|
||||||
Object setValue = properties.get(inKey);
|
|
||||||
if (setValue instanceof String) {
|
|
||||||
intVal.setString((String) setValue);
|
|
||||||
} else if (Number.class.isAssignableFrom(setValue
|
|
||||||
.getClass())) {
|
|
||||||
intVal.setObject(setValue);
|
|
||||||
} else {
|
|
||||||
intVal.setString(setValue.toString());
|
|
||||||
}
|
|
||||||
int value = intVal.get();
|
|
||||||
switch (validProp) {
|
|
||||||
case LockTimeout:
|
|
||||||
if (!fcPushed) {
|
|
||||||
fCfg = _broker.pushFetchConfiguration();
|
|
||||||
fcPushed = true;
|
|
||||||
}
|
|
||||||
fCfg.setLockTimeout(value);
|
|
||||||
break;
|
|
||||||
case ReadLockLevel:
|
|
||||||
if (value != MixedLockLevels.LOCK_NONE
|
|
||||||
&& value != fCfg.getReadLockLevel()) {
|
|
||||||
if (!fcPushed) {
|
|
||||||
fCfg = _broker.pushFetchConfiguration();
|
|
||||||
fcPushed = true;
|
|
||||||
}
|
|
||||||
fCfg.setReadLockLevel(value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WriteLockLevel:
|
|
||||||
if (value != MixedLockLevels.LOCK_NONE
|
|
||||||
&& value != fCfg.getWriteLockLevel()) {
|
|
||||||
if (!fcPushed) {
|
|
||||||
fCfg = _broker.pushFetchConfiguration();
|
|
||||||
fcPushed = true;
|
|
||||||
}
|
|
||||||
fCfg.setWriteLockLevel(value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// silently ignore the property
|
|
||||||
}
|
|
||||||
break; // for(String inKey : inKeys)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fcPushed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean pushLockProperties(LockModeType mode,
|
|
||||||
Map<String, Object> properties) {
|
|
||||||
boolean fcPushed = false;
|
|
||||||
// handle properties in map first
|
// handle properties in map first
|
||||||
if (properties != null) {
|
fPlan.addHints(properties);
|
||||||
fcPushed = setFetchConfigProperty(new FetchConfigProperty[] {
|
|
||||||
FetchConfigProperty.LockTimeout,
|
|
||||||
FetchConfigProperty.ReadLockLevel,
|
|
||||||
FetchConfigProperty.WriteLockLevel }, properties);
|
|
||||||
}
|
|
||||||
// override with the specific lockMode, if needed.
|
// override with the specific lockMode, if needed.
|
||||||
int setReadLevel = MixedLockLevelsHelper.toLockLevel(mode);
|
if (mode != null && mode != LockModeType.NONE) {
|
||||||
if (setReadLevel != MixedLockLevels.LOCK_NONE) {
|
|
||||||
// Set overriden read lock level
|
// Set overriden read lock level
|
||||||
FetchConfiguration fCfg = _broker.getFetchConfiguration();
|
LockModeType curReadLockMode = fPlan.getReadLockMode();
|
||||||
int curReadLevel = fCfg.getReadLockLevel();
|
if (mode != curReadLockMode)
|
||||||
if (setReadLevel != curReadLevel) {
|
fPlan.setReadLockMode(mode);
|
||||||
if (!fcPushed) {
|
|
||||||
fCfg = _broker.pushFetchConfiguration();
|
|
||||||
fcPushed = true;
|
|
||||||
}
|
|
||||||
fCfg.setReadLockLevel(setReadLevel);
|
|
||||||
}
|
|
||||||
// Set overriden isolation level for pessimistic-read/write
|
|
||||||
switch (setReadLevel) {
|
|
||||||
case MixedLockLevels.LOCK_PESSIMISTIC_READ:
|
|
||||||
fcPushed = setIsolationForPessimisticLock(fCfg, fcPushed,
|
|
||||||
Connection.TRANSACTION_REPEATABLE_READ);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MixedLockLevels.LOCK_PESSIMISTIC_WRITE:
|
|
||||||
case MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT:
|
|
||||||
fcPushed = setIsolationForPessimisticLock(fCfg, fcPushed,
|
|
||||||
Connection.TRANSACTION_SERIALIZABLE);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fcPushed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean setIsolationForPessimisticLock(FetchConfiguration fCfg,
|
|
||||||
boolean fcPushed, int level) {
|
|
||||||
if (!fcPushed) {
|
|
||||||
fCfg = _broker.pushFetchConfiguration();
|
|
||||||
fcPushed = true;
|
|
||||||
}
|
|
||||||
// TODO: refactoring under OPENJPA-957
|
|
||||||
// ((JDBCFetchConfiguration) fCfg).setIsolation(level);
|
|
||||||
return fcPushed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void popLockProperties(boolean fcPushed) {
|
|
||||||
if (fcPushed) {
|
|
||||||
_broker.popFetchConfiguration();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Metamodel getMetamodel() {
|
public Metamodel getMetamodel() {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"JPA 2.0 - Method not yet implemented");
|
"JPA 2.0 - Method not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refresh(Object arg0, Map<String, Object> arg1) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"JPA 2.0 - Method not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProperty(String arg0, Object arg1) {
|
public void setProperty(String arg0, Object arg1) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"JPA 2.0 - Method not yet implemented");
|
"JPA 2.0 - Method not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T find(Class<T> arg0, Object arg1, Map<String, Object> arg2) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"JPA 2.0 - Method not yet implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,29 @@ public interface FetchPlan {
|
||||||
*/
|
*/
|
||||||
public void setHint(String key, Object value);
|
public void setHint(String key, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hint for the given key to the given value.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public void setHint(String key, Object value, boolean validThrowException);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the hint and the associated value to the list.
|
||||||
|
*
|
||||||
|
* @param name the name of the hint
|
||||||
|
* @param value the value of the hint
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public void addHint(String name, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hint keys and values currently set of this receiver.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public void addHints(Map<String, Object> hints);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the hint keys and values currently set of this receiver.
|
* Gets the hint keys and values currently set of this receiver.
|
||||||
*
|
*
|
||||||
|
@ -291,10 +314,10 @@ public interface FetchPlan {
|
||||||
public int getLockTimeout();
|
public int getLockTimeout();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of milliseconds to wait for a query, or -1 for no
|
* The number of milliseconds to wait for an object lock, or -1 for no
|
||||||
* limit.
|
* limit.
|
||||||
*/
|
*/
|
||||||
public FetchPlan setQueryTimeout(int timeout);
|
public FetchPlan setLockTimeout(int timeout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of milliseconds to wait for a query, or -1 for no
|
* The number of milliseconds to wait for a query, or -1 for no
|
||||||
|
@ -303,11 +326,11 @@ public interface FetchPlan {
|
||||||
public int getQueryTimeout();
|
public int getQueryTimeout();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of milliseconds to wait for an object lock, or -1 for no
|
* The number of milliseconds to wait for a query, or -1 for no
|
||||||
* limit.
|
* limit.
|
||||||
*/
|
*/
|
||||||
|
public FetchPlan setQueryTimeout(int timeout);
|
||||||
|
|
||||||
public FetchPlan setLockTimeout(int timeout);
|
|
||||||
/**
|
/**
|
||||||
* The lock level to use for locking loaded objects.
|
* The lock level to use for locking loaded objects.
|
||||||
*/
|
*/
|
||||||
|
@ -328,7 +351,6 @@ public interface FetchPlan {
|
||||||
*/
|
*/
|
||||||
public FetchPlan setWriteLockMode(LockModeType mode);
|
public FetchPlan setWriteLockMode(LockModeType mode);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated cast to {@link FetchPlanImpl} instead. This
|
* @deprecated cast to {@link FetchPlanImpl} instead. This
|
||||||
* method pierces the published-API boundary, as does the SPI cast.
|
* method pierces the published-API boundary, as does the SPI cast.
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public class FetchPlanHintHandler extends AbstractHintHandler {
|
||||||
|
|
||||||
|
private static final Localizer _loc = Localizer
|
||||||
|
.forPackage(FetchPlanHintHandler.class);
|
||||||
|
|
||||||
|
protected static final String PREFIX_JPA = "javax.persistence.";
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
// Initiali javax.persistence to openjpa.FetchPlan hint mapping.
|
||||||
|
JavaxHintsMap.put(PREFIX_JPA + "lock.timeout", PREFIX_FETCHPLAN
|
||||||
|
+ "LockTimeout");
|
||||||
|
JavaxHintsMap.put(PREFIX_JPA + "query.timeout", PREFIX_FETCHPLAN
|
||||||
|
+ "QueryTimeout");
|
||||||
|
// Initialize hint precendent order mapping from list.
|
||||||
|
String[][] precedenceMapList = {
|
||||||
|
{ PREFIX_JPA + "lock.timeout",
|
||||||
|
PREFIX_FETCHPLAN + "LockTimeout",
|
||||||
|
PREFIX_OPENJPA + "LockTimeout" },
|
||||||
|
|
||||||
|
{ PREFIX_JPA + "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 (!hintName.startsWith(PREFIX_JPA)
|
||||||
|
&& !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),
|
||||||
|
false);
|
||||||
|
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_OPTIMISTIC
|
||||||
|
&& 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,6 @@ import javax.persistence.LockModeType;
|
||||||
|
|
||||||
import org.apache.openjpa.kernel.DelegatingFetchConfiguration;
|
import org.apache.openjpa.kernel.DelegatingFetchConfiguration;
|
||||||
import org.apache.openjpa.kernel.FetchConfiguration;
|
import org.apache.openjpa.kernel.FetchConfiguration;
|
||||||
import org.apache.openjpa.kernel.MixedLockLevels;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements FetchPlan via delegation to FetchConfiguration.
|
* Implements FetchPlan via delegation to FetchConfiguration.
|
||||||
|
@ -42,13 +41,14 @@ public class FetchPlanImpl
|
||||||
implements FetchPlan {
|
implements FetchPlan {
|
||||||
|
|
||||||
private final DelegatingFetchConfiguration _fetch;
|
private final DelegatingFetchConfiguration _fetch;
|
||||||
|
private FetchPlanHintHandler _hintHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor; supply delegate.
|
* Constructor; supply delegate.
|
||||||
*/
|
*/
|
||||||
public FetchPlanImpl(FetchConfiguration fetch) {
|
public FetchPlanImpl(FetchConfiguration fetch) {
|
||||||
_fetch = newDelegatingFetchConfiguration(fetch);
|
_fetch = newDelegatingFetchConfiguration(fetch);
|
||||||
|
_hintHandler = new FetchPlanHintHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,8 +275,24 @@ public class FetchPlanImpl
|
||||||
return _fetch.getHint(key);
|
return _fetch.getHint(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addHint(String key, Object value) {
|
||||||
|
_fetch.addHint(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
public void setHint(String key, Object value) {
|
public void setHint(String key, Object value) {
|
||||||
_fetch.setHint(key, value);
|
setHint(key, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHint(String key, Object value, boolean validThrowException) {
|
||||||
|
if( _hintHandler.setHint(key, value, validThrowException) )
|
||||||
|
_fetch.addHint(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHints(Map<String, Object> hints) {
|
||||||
|
if (hints != null && hints.size() > 0) {
|
||||||
|
for (String name : hints.keySet())
|
||||||
|
setHint(name, hints.get(name), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getHints() {
|
public Map<String, Object> getHints() {
|
||||||
|
|
|
@ -1,11 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
package org.apache.openjpa.persistence;
|
||||||
|
|
||||||
import static org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
|
import static org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
|
||||||
import static org.apache.openjpa.kernel.QueryHints.
|
import static org.apache.openjpa.kernel.QueryHints
|
||||||
HINT_INVALIDATE_PREPARED_QUERY;
|
.HINT_INVALIDATE_PREPARED_QUERY;
|
||||||
import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
|
import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -26,7 +43,6 @@ import org.apache.openjpa.lib.log.Log;
|
||||||
import org.apache.openjpa.lib.util.Localizer;
|
import org.apache.openjpa.lib.util.Localizer;
|
||||||
import org.apache.openjpa.lib.util.StringDistance;
|
import org.apache.openjpa.lib.util.StringDistance;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages query hint keys and handles their values on behalf of a owning
|
* Manages query hint keys and handles their values on behalf of a owning
|
||||||
* {@link QueryImpl}. Uses specific knowledge of hint keys declared in
|
* {@link QueryImpl}. Uses specific knowledge of hint keys declared in
|
||||||
|
@ -81,15 +97,16 @@ import org.apache.openjpa.lib.util.StringDistance;
|
||||||
*
|
*
|
||||||
* @nojavadoc
|
* @nojavadoc
|
||||||
*/
|
*/
|
||||||
public class HintHandler {
|
public class HintHandler extends FetchPlanHintHandler {
|
||||||
|
|
||||||
|
private static final Localizer _loc = Localizer.forPackage(
|
||||||
|
HintHandler.class);
|
||||||
|
|
||||||
private final QueryImpl owner;
|
private final QueryImpl owner;
|
||||||
private Map<String, Object> _hints;
|
private Map<String, Object> _hints;
|
||||||
private Set<String> _supportedKeys;
|
private Set<String> _supportedKeys;
|
||||||
private Set<String> _supportedPrefixes;
|
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.
|
// These keys are directly handled in {@link QueryImpl} class.
|
||||||
// Declaring a public static final String variable in this class will
|
// Declaring a public static final String variable in this class will
|
||||||
// make it register as a supported hint key
|
// make it register as a supported hint key
|
||||||
|
@ -103,21 +120,8 @@ public class HintHandler {
|
||||||
public static final String HINT_AGGREGATE_LISTENERS =
|
public static final String HINT_AGGREGATE_LISTENERS =
|
||||||
"openjpa.AggregateListeners";
|
"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, "QueryTimeout"));
|
|
||||||
_jpaKeys.put(addPrefix(PREFIX_JPA, "lock.timeout"),
|
|
||||||
addPrefix(PREFIX_FETCHPLAN, "LockTimeout"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String DOT = ".";
|
|
||||||
private static final String BLANK = "";
|
|
||||||
private static final Localizer _loc = Localizer.forPackage(
|
|
||||||
HintHandler.class);
|
|
||||||
|
|
||||||
HintHandler(QueryImpl impl) {
|
HintHandler(QueryImpl impl) {
|
||||||
|
super((FetchPlanImpl)impl.getFetchPlan());
|
||||||
owner = impl;
|
owner = impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,9 +155,8 @@ public class HintHandler {
|
||||||
.getLog(OpenJPAConfiguration.LOG_RUNTIME);
|
.getLog(OpenJPAConfiguration.LOG_RUNTIME);
|
||||||
String possible = StringDistance.getClosestLevenshteinDistance(hint,
|
String possible = StringDistance.getClosestLevenshteinDistance(hint,
|
||||||
getSupportedHints());
|
getSupportedHints());
|
||||||
if (log.isWarnEnabled()) {
|
if (log.isWarnEnabled())
|
||||||
log.warn(_loc.get("bad-query-hint", hint, possible));
|
log.warn(_loc.get("bad-query-hint", hint, possible));
|
||||||
}
|
|
||||||
return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
|
return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +179,7 @@ public class HintHandler {
|
||||||
Reflection.getBeanStylePropertyNames(
|
Reflection.getBeanStylePropertyNames(
|
||||||
owner.getFetchPlan().getClass())));
|
owner.getFetchPlan().getClass())));
|
||||||
|
|
||||||
_supportedKeys.addAll(_jpaKeys.keySet());
|
_supportedKeys.addAll(JavaxHintsMap.keySet());
|
||||||
|
|
||||||
_supportedKeys.addAll(Reflection.getFieldValues(
|
_supportedKeys.addAll(Reflection.getFieldValues(
|
||||||
HintHandler.class,
|
HintHandler.class,
|
||||||
|
@ -212,155 +215,74 @@ public class HintHandler {
|
||||||
return getSupportedHints().contains(key);
|
return getSupportedHints().contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Affirms the given key has a prefix that matches with any of the
|
|
||||||
* supported prefixes.
|
|
||||||
*/
|
|
||||||
private 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isKnownHintPrefix(String key) {
|
private boolean isKnownHintPrefix(String key) {
|
||||||
String prefix = getPrefixOf(key);
|
String prefix = getPrefixOf(key);
|
||||||
return getKnownPrefixes().contains(prefix);
|
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) {
|
public void setHint(String key, Object value) {
|
||||||
owner.lock();
|
owner.lock();
|
||||||
try {
|
try {
|
||||||
setHintInternal(key, value);
|
Boolean record = record(key, value);
|
||||||
|
if (record == Boolean.FALSE)
|
||||||
|
return;
|
||||||
|
FetchPlan plan = owner.getFetchPlan();
|
||||||
|
if (record == null) {
|
||||||
|
plan.setHint(key, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// request to throw IllegalArgumentException, if needed.
|
||||||
|
if (setHint(key, value, true))
|
||||||
|
plan.addHint(key, value);
|
||||||
} finally {
|
} finally {
|
||||||
owner.unlock();
|
owner.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setHintInternal(String key, Object value) {
|
protected boolean setHintInternal(String key, Object value,
|
||||||
Boolean record = record(key, value);
|
boolean validateThrowException) {
|
||||||
FetchPlan plan = owner.getFetchPlan();
|
|
||||||
ClassLoader loader = owner.getDelegate().getBroker().getClassLoader();
|
ClassLoader loader = owner.getDelegate().getBroker().getClassLoader();
|
||||||
if (record == Boolean.FALSE)
|
FetchPlan fPlan = owner.getFetchPlan();
|
||||||
return;
|
boolean objectSet = true;
|
||||||
if (record == null) {
|
if (HINT_SUBCLASSES.equals(key)) {
|
||||||
plan.setHint(key, value);
|
if (value instanceof String)
|
||||||
return;
|
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 (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);
|
||||||
|
fPlan.setHint(key, v);
|
||||||
|
objectSet = false;
|
||||||
|
} else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
|
||||||
|
fPlan.setHint(key, Filters.convert(value, Boolean.class));
|
||||||
|
owner.invalidatePreparedQuery();
|
||||||
|
objectSet = false;
|
||||||
|
} else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
|
||||||
|
fPlan.setHint(key, Filters.convert(value, Boolean.class));
|
||||||
|
owner.ignorePreparedQuery();
|
||||||
|
objectSet = false;
|
||||||
|
} else { // default
|
||||||
|
fPlan.setHint(key, value);
|
||||||
|
objectSet = false;
|
||||||
}
|
}
|
||||||
try {
|
return objectSet;
|
||||||
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)) {
|
|
||||||
if (requiresTransaction(key))
|
|
||||||
plan.setHint(key, value);
|
|
||||||
else
|
|
||||||
hintToSetter(plan, 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);
|
|
||||||
plan.setHint(key, v);
|
|
||||||
} else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
|
|
||||||
plan.setHint(key, Filters.convert(value, Boolean.class));
|
|
||||||
owner.invalidatePreparedQuery();
|
|
||||||
} else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
|
|
||||||
plan.setHint(key, Filters.convert(value, Boolean.class));
|
|
||||||
owner.ignorePreparedQuery();
|
|
||||||
} else { // default
|
|
||||||
plan.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 boolean requiresTransaction(String key) {
|
|
||||||
return key.endsWith("LockMode");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 static class HintKeyComparator implements Comparator<String> {
|
||||||
|
@ -377,7 +299,21 @@ public class HintHandler {
|
||||||
if (s == null || s.length() == 0)
|
if (s == null || s.length() == 0)
|
||||||
return 0;
|
return 0;
|
||||||
int index = s.indexOf(DOT);
|
int index = s.indexOf(DOT);
|
||||||
return (index == -1) ? 0 : countDots(s.substring(index+1)) + 1;
|
return (index == -1) ? 0 : countDots(s.substring(index + 1)) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String hintToKey(String key) {
|
||||||
|
// Let superclass performs key transformation when fPlan.setHint()
|
||||||
|
// is called.
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,10 @@ public class MixedLockLevelsHelper {
|
||||||
if (mode == null || mode == LockModeType.NONE)
|
if (mode == null || mode == LockModeType.NONE)
|
||||||
return MixedLockLevels.LOCK_NONE;
|
return MixedLockLevels.LOCK_NONE;
|
||||||
if (mode == LockModeType.READ || mode == LockModeType.OPTIMISTIC)
|
if (mode == LockModeType.READ || mode == LockModeType.OPTIMISTIC)
|
||||||
return MixedLockLevels.LOCK_READ;
|
return MixedLockLevels.LOCK_OPTIMISTIC;
|
||||||
if (mode == LockModeType.WRITE
|
if (mode == LockModeType.WRITE
|
||||||
|| mode == LockModeType.OPTIMISTIC_FORCE_INCREMENT)
|
|| mode == LockModeType.OPTIMISTIC_FORCE_INCREMENT)
|
||||||
return MixedLockLevels.LOCK_WRITE;
|
return MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT;
|
||||||
if (mode == LockModeType.PESSIMISTIC_READ)
|
if (mode == LockModeType.PESSIMISTIC_READ)
|
||||||
return MixedLockLevels.LOCK_PESSIMISTIC_READ;
|
return MixedLockLevels.LOCK_PESSIMISTIC_READ;
|
||||||
if (mode == LockModeType.PESSIMISTIC_WRITE)
|
if (mode == LockModeType.PESSIMISTIC_WRITE)
|
||||||
|
@ -53,11 +53,11 @@ public class MixedLockLevelsHelper {
|
||||||
*/
|
*/
|
||||||
public static LockModeType fromLockLevel(int level) {
|
public static LockModeType fromLockLevel(int level) {
|
||||||
if (level < MixedLockLevels.LOCK_OPTIMISTIC)
|
if (level < MixedLockLevels.LOCK_OPTIMISTIC)
|
||||||
return null;
|
return LockModeType.NONE;
|
||||||
if (level < MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT)
|
if (level < MixedLockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT)
|
||||||
return LockModeType.READ;
|
return LockModeType.OPTIMISTIC;
|
||||||
if (level < MixedLockLevels.LOCK_PESSIMISTIC_READ)
|
if (level < MixedLockLevels.LOCK_PESSIMISTIC_READ)
|
||||||
return LockModeType.WRITE;
|
return LockModeType.OPTIMISTIC_FORCE_INCREMENT;
|
||||||
if (level < MixedLockLevels.LOCK_PESSIMISTIC_WRITE)
|
if (level < MixedLockLevels.LOCK_PESSIMISTIC_WRITE)
|
||||||
return LockModeType.PESSIMISTIC_READ;
|
return LockModeType.PESSIMISTIC_READ;
|
||||||
if (level < MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT)
|
if (level < MixedLockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT)
|
||||||
|
|
|
@ -169,3 +169,7 @@ unwrap-em-invalid: EntityManager can not be unwrapped to an instance of "{0}".
|
||||||
unwrap-query-invalid: Query can not be unwrapped to an instance of "{0}".
|
unwrap-query-invalid: Query can not be unwrapped to an instance of "{0}".
|
||||||
invalid_entity_argument: Object being locked must be an valid and not detached \
|
invalid_entity_argument: Object being locked must be an valid and not detached \
|
||||||
entity.
|
entity.
|
||||||
|
bad-lock-level: Invalid lock mode/level. Valid values are \
|
||||||
|
"none"(0), "optimistic"(10), "optimistic-force-increment"(20), \
|
||||||
|
"pessimistic-read"(30), "pessimistic-write"(40) or \
|
||||||
|
"pessimistic-force-increment"(50). Specified value: {0}.
|
||||||
|
|
|
@ -814,6 +814,40 @@ hints={ @QueryHint (name="openjpa.hint.OptimizeResultCount", value="2"),
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</example>
|
</example>
|
||||||
</section>
|
</section>
|
||||||
|
<section id="multi-hints-handling">
|
||||||
|
<title>
|
||||||
|
Handling of Multiple Similar Query Hints
|
||||||
|
</title>
|
||||||
|
<para>
|
||||||
|
When similar hints in different prefix scopes are specified in a query,
|
||||||
|
the following prefix precedence order is used to select the effective hint:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
javax.persistence.*
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
openjpa.FetchPlan.*
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
openjpa.jdbc.*
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
openjpa.*
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
<example id="multi-hints-example">
|
||||||
|
<programlisting>
|
||||||
|
...
|
||||||
|
Query q = em.createQuery(.....);
|
||||||
|
q.setHint("openjpa.FetchPlan.LockTimeout", 1000);
|
||||||
|
q.setHint("javax.persistence.lock.timeout", 2000);
|
||||||
|
q.setHint("openjpa.LockTimeout", 3000);
|
||||||
|
// Lock time out of 2000 ms is in effect for query q
|
||||||
|
...
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<section id="jpa_overview_query_ordering">
|
<section id="jpa_overview_query_ordering">
|
||||||
<title>
|
<title>
|
||||||
|
|
|
@ -106,6 +106,15 @@ persistence.xml</filename>.
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
|
In JPA, The <classname>Map</classname> passed to the methods defined in the
|
||||||
|
<classname>Query</classname> and <classname>EntityManager</classname> interfaces
|
||||||
|
at runtime also overrides previous settings, including properties defined in
|
||||||
|
<filename>persistence.xml</filename>. The <classname>Map</classname> is in
|
||||||
|
effect only during the method invocation.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
When using JCA deployment the <literal>config-property</literal> values in your
|
When using JCA deployment the <literal>config-property</literal> values in your
|
||||||
<filename>ra.xml</filename> file override other settings.
|
<filename>ra.xml</filename> file override other settings.
|
||||||
</para>
|
</para>
|
||||||
|
|
Loading…
Reference in New Issue