OPENJPA-182. Changed JDBCFetchPlan.setIsolation() to use enums; added logic to handle enum hints to QueryImpl; moved from IllegalArgumentException to InvalidArgumentException to unify exception processing for both queries and find calls.

git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@526316 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2007-04-06 23:22:06 +00:00
parent 82d274abd6
commit 840644fcc5
6 changed files with 140 additions and 42 deletions

View File

@ -96,6 +96,7 @@ import org.apache.openjpa.util.Serialization;
import org.apache.openjpa.util.StoreException;
import org.apache.openjpa.util.UnsupportedException;
import org.apache.openjpa.util.UserException;
import org.apache.openjpa.util.InvalidStateException;
import serp.util.Numbers;
import serp.util.Strings;
@ -2156,9 +2157,8 @@ public class DBDictionary
protected String getForUpdateClause(JDBCFetchConfiguration fetch,
boolean forUpdate) {
if (fetch != null && fetch.getIsolation() != -1) {
throw new IllegalStateException(_loc.get(
"isolation-level-config-not-supported", getClass().getName())
.getMessage());
throw new InvalidStateException(_loc.get(
"isolation-level-config-not-supported", getClass().getName()));
} else if (forUpdate && !simulateLocking) {
assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate");
return forUpdateClause;

View File

@ -0,0 +1,71 @@
/*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed 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.jdbc;
import java.sql.Connection;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
/**
* Isolation levels for use in {@link JDBCFetchPlan#setIsolation}.
*
* @since 0.9.7
*/
public enum IsolationLevel {
DEFAULT(-1),
NONE(Connection.TRANSACTION_NONE),
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
private final int _connectionConstant;
private IsolationLevel(int connectionConstant) {
_connectionConstant = connectionConstant;
}
protected int getConnectionConstant() {
return _connectionConstant;
}
protected static IsolationLevel fromConnectionConstant(int constant) {
switch(constant) {
case -1:
case JDBCFetchConfiguration.DEFAULT:
return DEFAULT;
case Connection.TRANSACTION_NONE:
return NONE;
case Connection.TRANSACTION_READ_UNCOMMITTED:
return READ_UNCOMMITTED;
case Connection.TRANSACTION_READ_COMMITTED:
return READ_COMMITTED;
case Connection.TRANSACTION_REPEATABLE_READ:
return REPEATABLE_READ;
case Connection.TRANSACTION_SERIALIZABLE:
return SERIALIZABLE;
default:
throw new IllegalArgumentException(
Integer.valueOf(constant).toString());
}
}
}

View File

@ -15,8 +15,6 @@
*/
package org.apache.openjpa.persistence.jdbc;
import java.sql.Connection;
import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
import org.apache.openjpa.jdbc.kernel.LRSSizes;
import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
@ -118,36 +116,20 @@ public interface JDBCFetchPlan
public JDBCFetchPlan setJoinSyntax(int syntax);
/**
* <p>The isolation level for queries issued to the database. This overrides
* The isolation level for queries issued to the database. This overrides
* the persistence-unit-wide <code>openjpa.jdbc.TransactionIsolation</code>
* value.</p>
*
* <p>Must be one of {@link Connection#TRANSACTION_NONE},
* {@link Connection#TRANSACTION_READ_UNCOMMITTED},
* {@link Connection#TRANSACTION_READ_COMMITTED},
* {@link Connection#TRANSACTION_REPEATABLE_READ},
* {@link Connection#TRANSACTION_SERIALIZABLE},
* or -1 for the default connection level specified by the context in
* which this fetch plan is being used.</p>
* value.
*
* @since 0.9.7
*/
public int getIsolation();
public IsolationLevel getIsolation();
/**
* <p>The isolation level for queries issued to the database. This overrides
* The isolation level for queries issued to the database. This overrides
* the persistence-unit-wide <code>openjpa.jdbc.TransactionIsolation</code>
* value.</p>
*
* <p>Must be one of {@link Connection#TRANSACTION_NONE},
* {@link Connection#TRANSACTION_READ_UNCOMMITTED},
* {@link Connection#TRANSACTION_READ_COMMITTED},
* {@link Connection#TRANSACTION_REPEATABLE_READ},
* {@link Connection#TRANSACTION_SERIALIZABLE},
* or -1 for the default connection level specified by the context in
* which this fetch plan is being used.</p>
* value.
*
* @since 0.9.7
*/
public JDBCFetchPlan setIsolation(int level);
public JDBCFetchPlan setIsolation(IsolationLevel level);
}

View File

@ -103,12 +103,12 @@ public class JDBCFetchPlanImpl
return this;
}
public int getIsolation() {
return _fetch.getIsolation();
public IsolationLevel getIsolation() {
return IsolationLevel.fromConnectionConstant(_fetch.getIsolation());
}
public JDBCFetchPlan setIsolation(int level) {
_fetch.setIsolation(level);
public JDBCFetchPlan setIsolation(IsolationLevel level) {
_fetch.setIsolation(level.getConnectionConstant());
return this;
}
}

View File

@ -15,13 +15,14 @@
*/
package org.apache.openjpa.persistence.jdbc;
import java.sql.Connection;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
import org.apache.openjpa.persistence.simple.AllFieldTypes;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAQuery;
import org.apache.openjpa.persistence.InvalidStateException;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.DB2Dictionary;
import org.apache.openjpa.jdbc.sql.HSQLDictionary;
@ -36,7 +37,20 @@ public class TestIsolationLevelOverride
"openjpa.LockManager", "pessimistic");
}
public void testIsolationLevelOverride() {
public void testIsolationOverrideViaFetchPlan() {
testIsolationLevelOverride(false, false);
}
public void testIsolationOverrideViaHint() {
testIsolationLevelOverride(true, false);
}
public void testIsolationOverrideViaStringHint() {
testIsolationLevelOverride(true, true);
}
public void testIsolationLevelOverride(boolean useHintsAndQueries,
boolean useStringHints) {
OpenJPAEntityManager em =
OpenJPAPersistence.cast(emf.createEntityManager());
DBDictionary dict = ((JDBCConfiguration) em.getConfiguration())
@ -49,9 +63,27 @@ public class TestIsolationLevelOverride
sql.clear();
try {
em.getTransaction().begin();
((JDBCFetchPlan) em.getFetchPlan())
.setIsolation(Connection.TRANSACTION_SERIALIZABLE);
em.find(AllFieldTypes.class, 0);
if (useHintsAndQueries) {
Query q = em.createQuery(
"select o from AllFieldTypes o where o.intField = :p");
q.setParameter("p", 0);
if (useStringHints) {
q.setHint("openjpa.FetchPlan.Isolation", "SERIALIZABLE");
} else {
q.setHint("openjpa.FetchPlan.Isolation",
IsolationLevel.SERIALIZABLE);
}
assertEquals(IsolationLevel.SERIALIZABLE,
((JDBCFetchPlan) ((OpenJPAQuery) q).getFetchPlan())
.getIsolation());
q.getResultList();
} else {
((JDBCFetchPlan) em.getFetchPlan())
.setIsolation(IsolationLevel.SERIALIZABLE);
em.find(AllFieldTypes.class, 0);
}
if (dict instanceof DB2Dictionary) {
assertEquals(1, sql.size());
@ -60,10 +92,9 @@ public class TestIsolationLevelOverride
fail("OpenJPA currently only supports per-query isolation " +
"level configuration on the following databases: DB2");
}
} catch (PersistenceException pe) {
// if we're not using DB2, we expect an IllegalStateException.
if (dict instanceof DB2Dictionary
|| !(pe.getCause() instanceof IllegalStateException))
} catch (InvalidStateException pe) {
// if we're not using DB2, we expect an InvalidStateException.
if (dict instanceof DB2Dictionary)
throw pe;
} finally {
em.getTransaction().rollback();

View File

@ -26,6 +26,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.lang.reflect.Method;
import javax.persistence.FlushModeType;
import javax.persistence.Query;
import javax.persistence.TemporalType;
@ -38,6 +39,7 @@ import org.apache.openjpa.kernel.exps.AggregateListener;
import org.apache.openjpa.kernel.exps.FilterListener;
import org.apache.openjpa.lib.rop.ResultList;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.enhance.Reflection;
/**
* Implementation of {@link Query} interface.
@ -364,7 +366,7 @@ public class QueryImpl
addAggregateListener(arr[i]);
} else if (k.startsWith("FetchPlan.")) {
k = k.substring("FetchPlan.".length());
Filters.hintToSetter(getFetchPlan(), k, value);
hintToSetter(getFetchPlan(), k, value);
} else if (k.startsWith("hint.")) {
if ("hint.OptimizeResultCount".equals(k)) {
if (value instanceof String) {
@ -390,6 +392,18 @@ public class QueryImpl
}
}
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 OpenJPAQuery setParameter(int position, Calendar value,
TemporalType t) {
return setParameter(position, (Object) value);