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:
Albert Lee 2009-04-05 21:29:42 +00:00
parent 616f8fdbee
commit 718370e1ef
32 changed files with 8455 additions and 5139 deletions

View File

@ -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)

View File

@ -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:

View File

@ -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}.

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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.
* *

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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}.

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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()",

View File

@ -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

View File

@ -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();

View File

@ -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");
}
} }

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -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;
}
} }

View File

@ -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)

View File

@ -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}.

View File

@ -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>

View File

@ -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>