mirror of https://github.com/apache/openjpa.git
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:
parent
82d274abd6
commit
840644fcc5
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue