OPENJPA-182. Moved to API-based model. Query.setHint() can still be used via the query hint => fetch plan binding.

Removed the logic to override the forUpdate value, since the calling code already incorporates fetch configuration data into its decision about how to invoke toSelect(). Added a test case to assert this behavior.

Also cleaned up some minor whitespace issues, and reduced code duplication by moving a couple of concepts up into DBDictionary. Removed some seemingly-unnecessary overrides from H2Dictionary.

Added a test case for isolation level configuration. For non-DB2 dictionaries, it asserts that an exception is thrown during execution. Someone with DB2 knowledge / access should fill in the test case for the DB2 cases. As we add support for per-query isolation level configuration for other databases, we should change this test case.

git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@525997 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2007-04-05 22:55:52 +00:00
parent e541936463
commit 814637ca6c
13 changed files with 365 additions and 180 deletions

View File

@ -240,4 +240,21 @@ public class DelegatingJDBCFetchConfiguration
throw translate(re); throw translate(re);
} }
} }
public int getIsolationLevel() {
try {
return getJDBCDelegate().getIsolationLevel();
} catch (RuntimeException re) {
throw translate(re);
}
}
public JDBCFetchConfiguration setIsolationLevel(int level) {
try {
getJDBCDelegate().setIsolationLevel(level);
return this;
} catch (RuntimeException re) {
throw translate(re);
}
}
} }

View File

@ -16,6 +16,7 @@
package org.apache.openjpa.jdbc.kernel; package org.apache.openjpa.jdbc.kernel;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Connection;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
@ -169,4 +170,38 @@ public interface JDBCFetchConfiguration
* Convenience method to cast traversal to store-specific type. * Convenience method to cast traversal to store-specific type.
*/ */
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm); public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm);
/**
* <p>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 configuration is being used.</p>
*
* @since 0.9.7
*/
public int getIsolationLevel();
/**
* <p>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 configuration is being used.</p>
*
* @since 0.9.7
*/
public JDBCFetchConfiguration setIsolationLevel(int level);
} }

View File

@ -17,6 +17,7 @@ package org.apache.openjpa.jdbc.kernel;
import java.io.Serializable; import java.io.Serializable;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Connection;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -65,6 +66,7 @@ public class JDBCFetchConfigurationImpl
public int size = 0; public int size = 0;
public int syntax = 0; public int syntax = 0;
public Set joins = null; public Set joins = null;
public int isolationLevel = -1;
} }
private final JDBCConfigurationState _state; private final JDBCConfigurationState _state;
@ -319,4 +321,22 @@ public class JDBCFetchConfigurationImpl
return null; return null;
return (JDBCConfiguration) conf; return (JDBCConfiguration) conf;
} }
public int getIsolationLevel() {
return _state.isolationLevel;
}
public JDBCFetchConfiguration setIsolationLevel(int level) {
if (level != -1
&& level != Connection.TRANSACTION_NONE
&& level != Connection.TRANSACTION_READ_UNCOMMITTED
&& level != Connection.TRANSACTION_READ_COMMITTED
&& level != Connection.TRANSACTION_REPEATABLE_READ
&& level != Connection.TRANSACTION_SERIALIZABLE)
throw new IllegalArgumentException(
_loc.get("bad-level", Integer.valueOf(level)).getMessage());
_state.isolationLevel = level;
return this;
}
} }

View File

@ -23,8 +23,8 @@ import java.util.Arrays;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration; import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.schema.Sequence; import org.apache.openjpa.jdbc.schema.Sequence;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.util.OpenJPAException; import org.apache.openjpa.util.OpenJPAException;
import org.apache.openjpa.kernel.LockLevels;
/** /**
* Dictionary for IBM DB2 database. * Dictionary for IBM DB2 database.
@ -35,19 +35,20 @@ public class DB2Dictionary
public String optimizeClause = "optimize for"; public String optimizeClause = "optimize for";
public String rowClause = "row"; public String rowClause = "row";
private int db2ServerType = 0; private int db2ServerType = 0;
private static final int db2ISeriesV5R3AndEarlier = 1; private static final int db2ISeriesV5R3AndEarlier = 1;
private static final int db2UDBV81OrEarlier = 2; private static final int db2UDBV81OrEarlier = 2;
private static final int db2ZOSV8x = 3; private static final int db2ZOSV8x = 3;
private static final int db2UDBV82AndLater = 4; private static final int db2UDBV82AndLater = 4;
private static final int db2ISeriesV5R4AndLater = 5; private static final int db2ISeriesV5R4AndLater = 5;
private static final String forUpdateOfClause="FOR UPDATE OF"; private static final String forUpdateOfClause = "FOR UPDATE OF";
private static final String withRSClause="WITH RS"; private static final String withRSClause = "WITH RS";
private static final String withRRClause="WITH RR"; private static final String withRRClause = "WITH RR";
private static final String useKeepUpdateLockClause= "USE AND KEEP UPDATE LOCKS"; private static final String useKeepUpdateLockClause
private static final String useKeepExclusiveLockClause="USE AND KEEP EXCLUSIVE LOCKS"; = "USE AND KEEP UPDATE LOCKS";
private static final String forReadOnlyClause = "FOR READ ONLY"; private static final String useKeepExclusiveLockClause
public static final String UPDATE_HINT = "openjpa.hint.updateClause"; = "USE AND KEEP EXCLUSIVE LOCKS";
public static final String ISOLATION_HINT = "openjpa.hint.isolationLevel"; private static final String forReadOnlyClause = "FOR READ ONLY";
public DB2Dictionary() { public DB2Dictionary() {
platform = "DB2"; platform = "DB2";
validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM " validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM "
@ -189,15 +190,15 @@ public class DB2Dictionary
// Determine the type of DB2 database // Determine the type of DB2 database
if (isDB2ISeriesV5R3AndEarlier(metaData)) if (isDB2ISeriesV5R3AndEarlier(metaData))
db2ServerType =db2ISeriesV5R3AndEarlier; db2ServerType = db2ISeriesV5R3AndEarlier;
else if (isDB2UDBV81OrEarlier(metaData,maj,min)) else if (isDB2UDBV81OrEarlier(metaData,maj,min))
db2ServerType =db2UDBV81OrEarlier; db2ServerType = db2UDBV81OrEarlier;
else if (isDB2ZOSV8x(metaData,maj)) else if (isDB2ZOSV8x(metaData,maj))
db2ServerType =db2ZOSV8x; db2ServerType = db2ZOSV8x;
else if (isDB2UDBV82AndLater(metaData,maj,min)) else if (isDB2UDBV82AndLater(metaData,maj,min))
db2ServerType=db2UDBV82AndLater; db2ServerType = db2UDBV82AndLater;
else if (isDB2ISeriesV5R4AndLater(metaData)) else if (isDB2ISeriesV5R4AndLater(metaData))
db2ServerType=db2ISeriesV5R4AndLater; db2ServerType = db2ISeriesV5R4AndLater;
if (maj >= 9 || (maj == 8 && min >= 2)) { if (maj >= 9 || (maj == 8 && min >= 2)) {
supportsLockingWithMultipleTables = true; supportsLockingWithMultipleTables = true;
@ -226,65 +227,63 @@ public class DB2Dictionary
} }
} }
/** Get the update clause for the query based on the /**
* Get the update clause for the query based on the
* updateClause and isolationLevel hints * updateClause and isolationLevel hints
*/ */
public String getForUpdateClause(JDBCFetchConfiguration fetch, boolean forUpdate) { protected String getForUpdateClause(JDBCFetchConfiguration fetch,
String isolationLevel = null; boolean forUpdate) {
boolean updateClause; int isolationLevel;
DatabaseMetaData metaData = null;
StringBuffer forUpdateString = new StringBuffer(); StringBuffer forUpdateString = new StringBuffer();
try { try {
// Determine the update clause/isolationLevel the hint // Determine the isolationLevel; the fetch
// overrides the persistence.xml value // configuration data overrides the persistence.xml value
if (fetch != null && fetch.getHint(UPDATE_HINT) if (fetch != null && fetch.getIsolationLevel() != -1)
!=null ) isolationLevel = fetch.getIsolationLevel();
updateClause = ((Boolean)fetch.
getHint(UPDATE_HINT)).booleanValue();
else else
updateClause = forUpdate; isolationLevel = conf.getTransactionIsolationConstant();
if (fetch != null &&fetch.getHint(ISOLATION_HINT)
!=null ) if (!forUpdate) {
isolationLevel = (String)fetch.
getHint(ISOLATION_HINT);
else
isolationLevel = conf.getTransactionIsolation();
if (updateClause == false)
// This sql is not for update so add FOR Read Only clause // This sql is not for update so add FOR Read Only clause
forUpdateString.append(" ").append(forReadOnlyClause) forUpdateString.append(" ").append(forReadOnlyClause)
.append(" "); .append(" ");
else if (updateClause == true){ } else {
switch(db2ServerType){ switch(db2ServerType) {
case db2ISeriesV5R3AndEarlier: case db2ISeriesV5R3AndEarlier:
case db2UDBV81OrEarlier: case db2UDBV81OrEarlier:
if (isolationLevel.equals("read-uncommitted")) if (isolationLevel ==
Connection.TRANSACTION_READ_UNCOMMITTED) {
forUpdateString.append(" ").append(withRSClause) forUpdateString.append(" ").append(withRSClause)
.append(" ").append(forUpdateOfClause).append(" "); .append(" ").append(forUpdateOfClause).append(" ");
else } else {
forUpdateString.append(" ").append(forUpdateOfClause) forUpdateString.append(" ").append(forUpdateOfClause)
.append(" "); .append(" ");
}
break; break;
case db2ZOSV8x: case db2ZOSV8x:
case db2UDBV82AndLater: case db2UDBV82AndLater:
if (isolationLevel.equals("serializable")) if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) {
forUpdateString.append(" ").append(withRRClause) forUpdateString.append(" ").append(withRRClause)
.append(" ").append(useKeepUpdateLockClause) .append(" ").append(useKeepUpdateLockClause)
.append(" "); .append(" ");
else } else {
forUpdateString.append(" ").append(withRSClause) forUpdateString.append(" ").append(withRSClause)
.append(" ").append(useKeepUpdateLockClause) .append(" ").append(useKeepUpdateLockClause)
.append(" "); .append(" ");
}
break; break;
case db2ISeriesV5R4AndLater: case db2ISeriesV5R4AndLater:
if (isolationLevel.equals("serializable")) if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) {
forUpdateString.append(" ").append(withRRClause) forUpdateString.append(" ").append(withRRClause)
.append(" ").append(useKeepExclusiveLockClause) .append(" ").append(useKeepExclusiveLockClause)
.append(" "); .append(" ");
else } else {
forUpdateString.append(" ").append(withRSClause) forUpdateString.append(" ").append(withRSClause)
.append(" ").append(useKeepExclusiveLockClause) .append(" ").append(useKeepExclusiveLockClause)
.append(" "); .append(" ");
}
break;
} }
} }
} }
@ -299,16 +298,16 @@ public class DB2Dictionary
int min) throws SQLException { int min) throws SQLException {
boolean match = false; boolean match = false;
if (metadata.getDatabaseProductVersion().indexOf("SQL") != -1 if (metadata.getDatabaseProductVersion().indexOf("SQL") != -1
&& ((maj ==8 && min >=2) ||(maj >=8))) && ((maj == 8 && min >= 2) ||(maj >= 8)))
match = true; match = true;
return match; return match;
} }
public boolean isDB2ZOSV8x(DatabaseMetaData metadata,int maj) public boolean isDB2ZOSV8x(DatabaseMetaData metadata, int maj)
throws SQLException { throws SQLException {
boolean match = false; boolean match = false;
if (metadata.getDatabaseProductVersion().indexOf("DSN") != -1 if (metadata.getDatabaseProductVersion().indexOf("DSN") != -1
&& maj ==8 ) && maj == 8)
match = true; match = true;
return match; return match;
} }
@ -318,7 +317,7 @@ public class DB2Dictionary
boolean match = false; boolean match = false;
if (metadata.getDatabaseProductVersion().indexOf("AS") != -1 if (metadata.getDatabaseProductVersion().indexOf("AS") != -1
&& generateVersionNumber(metadata.getDatabaseProductVersion()) && generateVersionNumber(metadata.getDatabaseProductVersion())
<= 530 ) <= 530)
match = true; match = true;
return match; return match;
} }
@ -328,16 +327,16 @@ public class DB2Dictionary
boolean match = false; boolean match = false;
if (metadata.getDatabaseProductVersion().indexOf("AS") != -1 if (metadata.getDatabaseProductVersion().indexOf("AS") != -1
&& generateVersionNumber(metadata.getDatabaseProductVersion()) && generateVersionNumber(metadata.getDatabaseProductVersion())
>= 540 ) >= 540)
match = true; match = true;
return match; return match;
} }
public boolean isDB2UDBV81OrEarlier(DatabaseMetaData metadata,int maj, public boolean isDB2UDBV81OrEarlier(DatabaseMetaData metadata, int maj,
int min) throws SQLException { int min) throws SQLException {
boolean match = false; boolean match = false;
if (metadata.getDatabaseProductVersion().indexOf("SQL") != -1 && if (metadata.getDatabaseProductVersion().indexOf("SQL") != -1 &&
((maj ==8 && min <=1)|| maj <8 )) ((maj == 8 && min <= 1)|| maj < 8))
match = true; match = true;
return match; return match;
} }
@ -359,71 +358,16 @@ public class DB2Dictionary
return i; return i;
} }
/**
* Override the toOperationMethod of DBDictionary to pass the
* forUpdateString.
*/
protected SQLBuffer toOperation(String op, SQLBuffer selects,
SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having,
SQLBuffer order, boolean distinct, boolean forUpdate, long start,
long end,String forUpdateString) {
SQLBuffer buf = new SQLBuffer(this);
buf.append(op);
boolean range = start != 0 || end != Long.MAX_VALUE;
if (range && rangePosition == RANGE_PRE_DISTINCT)
appendSelectRange(buf, start, end);
if (distinct)
buf.append(" DISTINCT");
if (range && rangePosition == RANGE_POST_DISTINCT)
appendSelectRange(buf, start, end);
buf.append(" ").append(selects).append(" FROM ").append(from);
if (where != null && !where.isEmpty())
buf.append(" WHERE ").append(where);
if (group != null && !group.isEmpty())
buf.append(" GROUP BY ").append(group);
if (having != null && !having.isEmpty()) {
assertSupport(supportsHaving, "SupportsHaving");
buf.append(" HAVING ").append(having);
}
if (order != null && !order.isEmpty())
buf.append(" ORDER BY ").append(order);
if (range && rangePosition == RANGE_POST_SELECT)
appendSelectRange(buf, start, end);
if (!simulateLocking ) {
assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate");
buf.append(" ").append(forUpdateString);
}
if (range && rangePosition == RANGE_POST_LOCK)
appendSelectRange(buf, start, end);
return buf;
}
public SQLBuffer toSelect(Select sel, boolean forUpdate, public SQLBuffer toSelect(Select sel, boolean forUpdate,
JDBCFetchConfiguration fetch) { JDBCFetchConfiguration fetch) {
sel.addJoinClassConditions(); SQLBuffer buf = super.toSelect(sel, forUpdate, fetch);
boolean update = forUpdate && sel.getFromSelect() == null;
SQLBuffer select = getSelects(sel, false, update); if (sel.getExpectedResultCount() > 0) {
SQLBuffer ordering = null; buf.append(" ").append(optimizeClause).append(" ")
if (!sel.isAggregate() || sel.getGrouping() != null) .append(String.valueOf(sel.getExpectedResultCount()))
ordering = sel.getOrdering(); .append(" ").append(rowClause);
SQLBuffer from; }
if (sel.getFromSelect() != null)
from = getFromSelect(sel, forUpdate);
else
from = getFrom(sel, update);
SQLBuffer where = getWhere(sel, update);
String forUpdateString = getForUpdateClause(fetch,forUpdate);
SQLBuffer buf = toOperation(getSelectOperation(fetch), select,
from, where,sel.getGrouping(), sel.getHaving(), ordering,
sel.isDistinct(), forUpdate, sel.getStartIndex(),
sel.getEndIndex(),forUpdateString);
if (sel.getExpectedResultCount() > 0)
buf.append(" ").append(optimizeClause).append(" ").
append(String.valueOf(sel.getExpectedResultCount())).
append(" ").append(rowClause);
return buf; return buf;
} }

View File

@ -2145,7 +2145,22 @@ public class DBDictionary
SQLBuffer having, SQLBuffer order, SQLBuffer having, SQLBuffer order,
boolean distinct, boolean forUpdate, long start, long end) { boolean distinct, boolean forUpdate, long start, long end) {
return toOperation(getSelectOperation(fetch), selects, from, where, return toOperation(getSelectOperation(fetch), selects, from, where,
group, having, order, distinct, forUpdate, start, end); group, having, order, distinct, forUpdate, start, end,
getForUpdateClause(fetch, forUpdate));
}
/**
* Get the update clause for the query based on the
* updateClause and isolationLevel hints
*/
protected String getForUpdateClause(JDBCFetchConfiguration fetch,
boolean forUpdate) {
if (fetch.getIsolationLevel() != -1)
throw new IllegalStateException(_loc.get(
"isolation-level-config-not-supported", getClass().getName())
.getMessage());
else
return forUpdateClause;
} }
/** /**
@ -2161,7 +2176,7 @@ public class DBDictionary
protected SQLBuffer toOperation(String op, SQLBuffer selects, protected SQLBuffer toOperation(String op, SQLBuffer selects,
SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having, SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having,
SQLBuffer order, boolean distinct, boolean forUpdate, long start, SQLBuffer order, boolean distinct, boolean forUpdate, long start,
long end) { long end, String forUpdateClause) {
SQLBuffer buf = new SQLBuffer(this); SQLBuffer buf = new SQLBuffer(this);
buf.append(op); buf.append(op);
@ -2190,8 +2205,8 @@ public class DBDictionary
if (forUpdate && !simulateLocking) { if (forUpdate && !simulateLocking) {
assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate"); assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate");
if (forUpdateClause != null) if (this.forUpdateClause != null)
buf.append(" ").append(forUpdateClause); buf.append(" ").append(this.forUpdateClause);
} }
if (range && rangePosition == RANGE_POST_LOCK) if (range && rangePosition == RANGE_POST_LOCK)
appendSelectRange(buf, start, end); appendSelectRange(buf, start, end);

View File

@ -159,14 +159,6 @@ public class H2Dictionary extends DBDictionary {
return buf.toString(); return buf.toString();
} }
protected SQLBuffer toOperation(String op, SQLBuffer selects,
SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having,
SQLBuffer order, boolean distinct, boolean forUpdate, long start,
long end) {
return super.toOperation(op, selects, from, where, group, having,
order, distinct, forUpdate, start, end);
}
public Column[] getColumns(DatabaseMetaData meta, String catalog, public Column[] getColumns(DatabaseMetaData meta, String catalog,
String schemaName, String tableName, String columnName, Connection conn) String schemaName, String tableName, String columnName, Connection conn)
throws SQLException { throws SQLException {
@ -175,18 +167,6 @@ public class H2Dictionary extends DBDictionary {
return cols; return cols;
} }
public void setDouble(PreparedStatement stmnt, int idx, double val,
Column col)
throws SQLException {
super.setDouble(stmnt, idx, val, col);
}
public void setBigDecimal(PreparedStatement stmnt, int idx, BigDecimal val,
Column col)
throws SQLException {
super.setBigDecimal(stmnt, idx, val, col);
}
protected void appendSelectRange(SQLBuffer buf, long start, long end) { protected void appendSelectRange(SQLBuffer buf, long start, long end) {
if (end != Long.MAX_VALUE) if (end != Long.MAX_VALUE)
buf.append(" LIMIT ").appendValue(end - start); buf.append(" LIMIT ").appendValue(end - start);

View File

@ -193,13 +193,13 @@ public class HSQLDictionary
protected SQLBuffer toOperation(String op, SQLBuffer selects, protected SQLBuffer toOperation(String op, SQLBuffer selects,
SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having, SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having,
SQLBuffer order, boolean distinct, boolean forUpdate, long start, SQLBuffer order, boolean distinct, boolean forUpdate, long start,
long end) { long end, String forUpdateClause) {
// hsql requires ordering when limit is used // hsql requires ordering when limit is used
if ((start != 0 || end != Long.MAX_VALUE) if ((start != 0 || end != Long.MAX_VALUE)
&& (order == null || order.isEmpty())) && (order == null || order.isEmpty()))
order = _oneBuffer; order = _oneBuffer;
return super.toOperation(op, selects, from, where, group, having, return super.toOperation(op, selects, from, where, group, having,
order, distinct, forUpdate, start, end); order, distinct, forUpdate, start, end, forUpdateClause);
} }
public Column[] getColumns(DatabaseMetaData meta, String catalog, public Column[] getColumns(DatabaseMetaData meta, String catalog,

View File

@ -97,3 +97,8 @@ clstable-seq-usage: Usage: \
native-seq-usage: Usage: java org.apache.openjpa.jdbc.kernel.NativeJDBCSeq\n\ 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, \
Connection.TRANSACTION_NONE, Connection.TRANSACTION_READ_UNCOMMITTED, \
Connection.TRANSACTION_READ_COMMITTED, \
Connection.TRANSACTION_REPEATABLE_READ, or \
Connection.TRANSACTION_SERIALIZABLE. Specified value: {0}.

View File

@ -160,3 +160,5 @@ oracle-timestamp-bug: An ArrayIndexOutOfBoundsException has occured when \
less than 9.2. Downgrading the driver will solve this, or it can be \ less than 9.2. Downgrading the driver will solve this, or it can be \
worked around by setting the "SupportsTimestampNanos" DBDictionary \ worked around by setting the "SupportsTimestampNanos" DBDictionary \
property to "true". property to "true".
isolation-level-config-not-supported: This DBDictionary does not support \
customization of isolation levels on a per-query basis. DBDictionary: {0}.

View File

@ -15,6 +15,8 @@
*/ */
package org.apache.openjpa.persistence.jdbc; package org.apache.openjpa.persistence.jdbc;
import java.sql.Connection;
import org.apache.openjpa.jdbc.kernel.EagerFetchModes; import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
import org.apache.openjpa.jdbc.kernel.LRSSizes; import org.apache.openjpa.jdbc.kernel.LRSSizes;
import org.apache.openjpa.jdbc.sql.JoinSyntaxes; import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
@ -114,4 +116,38 @@ public interface JDBCFetchPlan
* @see JoinSyntaxes * @see JoinSyntaxes
*/ */
public JDBCFetchPlan setJoinSyntax(int syntax); public JDBCFetchPlan setJoinSyntax(int syntax);
/**
* <p>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>
*
* @since 0.9.7
*/
public int getIsolationLevel();
/**
* <p>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>
*
* @since 0.9.7
*/
public JDBCFetchPlan setIsolationLevel(int level);
} }

View File

@ -102,4 +102,13 @@ public class JDBCFetchPlanImpl
_fetch.setJoinSyntax(syntax); _fetch.setJoinSyntax(syntax);
return this; return this;
} }
public int getIsolationLevel() {
return _fetch.getIsolationLevel();
}
public JDBCFetchPlan setIsolationLevel(int level) {
_fetch.setIsolationLevel(level);
return this;
}
} }

View File

@ -0,0 +1,70 @@
/*
* 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 javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceException;
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.FetchPlan;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.DB2Dictionary;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
public class TestIsolationLevelOverride
extends SQLListenerTestCase {
public void setUp() {
setUp(AllFieldTypes.class,
"openjpa.Optimistic", "false",
"openjpa.LockManager", "pessimistic");
}
public void testIsolationLevelOverride() {
OpenJPAEntityManager em =
OpenJPAPersistence.cast(emf.createEntityManager());
DBDictionary dict = ((JDBCConfiguration) em.getConfiguration())
.getDBDictionaryInstance();
sql.clear();
try {
em.getTransaction().begin();
((JDBCFetchPlan) em.getFetchPlan())
.setIsolationLevel(Connection.TRANSACTION_SERIALIZABLE);
em.find(AllFieldTypes.class, 0);
if (dict instanceof DB2Dictionary) {
assertEquals(1, sql.size());
assertSQL(".*DB2-specific SQL to test for goes here.*");
} else {
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))
throw pe;
} finally {
em.getTransaction().rollback();
em.close();
}
}
}

View File

@ -0,0 +1,52 @@
/*
* 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 javax.persistence.EntityManager;
import javax.persistence.LockModeType;
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.FetchPlan;
public class TestSelectForUpdateOverride
extends SQLListenerTestCase {
public void setUp() {
setUp(AllFieldTypes.class,
"openjpa.Optimistic", "false",
"openjpa.LockManager", "pessimistic",
"openjpa.ReadLockLevel", "none");
}
public void testSelectForUpdateOverride() {
EntityManager em = emf.createEntityManager();
sql.clear();
try {
em.getTransaction().begin();
OpenJPAPersistence.cast(em).getFetchPlan()
.setReadLockMode(LockModeType.WRITE);
em.find(AllFieldTypes.class, 0);
assertEquals(1, sql.size());
assertSQL(".*FOR UPDATE.*");
} finally {
em.getTransaction().rollback();
em.close();
}
}
}