diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java index 405df5aa8..98ecba8ea 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/DelegatingJDBCFetchConfiguration.java @@ -240,4 +240,21 @@ public class DelegatingJDBCFetchConfiguration 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); + } + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java index b18aacc0e..a1389827a 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfiguration.java @@ -16,6 +16,7 @@ package org.apache.openjpa.jdbc.kernel; import java.sql.ResultSet; +import java.sql.Connection; import java.util.Collection; import java.util.Set; @@ -169,4 +170,38 @@ public interface JDBCFetchConfiguration * Convenience method to cast traversal to store-specific type. */ public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm); + + /** + *
The isolation level for queries issued to the database. This overrides
+ * the persistence-unit-wide openjpa.jdbc.TransactionIsolation
+ * value.
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.
+ * + * @since 0.9.7 + */ + public int getIsolationLevel(); + + /** + *The isolation level for queries issued to the database. This overrides
+ * the persistence-unit-wide openjpa.jdbc.TransactionIsolation
+ * value.
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.
+ * + * @since 0.9.7 + */ + public JDBCFetchConfiguration setIsolationLevel(int level); } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java index 3ef8f7043..669888dde 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCFetchConfigurationImpl.java @@ -17,6 +17,7 @@ package org.apache.openjpa.jdbc.kernel; import java.io.Serializable; import java.sql.ResultSet; +import java.sql.Connection; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -65,6 +66,7 @@ public class JDBCFetchConfigurationImpl public int size = 0; public int syntax = 0; public Set joins = null; + public int isolationLevel = -1; } private final JDBCConfigurationState _state; @@ -319,4 +321,22 @@ public class JDBCFetchConfigurationImpl return null; 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; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java index 66147ab50..e3a7de30b 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java @@ -23,8 +23,8 @@ import java.util.Arrays; import java.util.StringTokenizer; import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration; import org.apache.openjpa.jdbc.schema.Sequence; -import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.util.OpenJPAException; +import org.apache.openjpa.kernel.LockLevels; /** * Dictionary for IBM DB2 database. @@ -34,20 +34,21 @@ public class DB2Dictionary public String optimizeClause = "optimize for"; public String rowClause = "row"; - private int db2ServerType = 0; - private static final int db2ISeriesV5R3AndEarlier = 1; + private int db2ServerType = 0; + private static final int db2ISeriesV5R3AndEarlier = 1; private static final int db2UDBV81OrEarlier = 2; private static final int db2ZOSV8x = 3; private static final int db2UDBV82AndLater = 4; - private static final int db2ISeriesV5R4AndLater = 5; - private static final String forUpdateOfClause="FOR UPDATE OF"; - private static final String withRSClause="WITH RS"; - private static final String withRRClause="WITH RR"; - private static final String useKeepUpdateLockClause= "USE AND KEEP UPDATE LOCKS"; - private static final String useKeepExclusiveLockClause="USE AND KEEP EXCLUSIVE LOCKS"; - private static final String forReadOnlyClause = "FOR READ ONLY"; - public static final String UPDATE_HINT = "openjpa.hint.updateClause"; - public static final String ISOLATION_HINT = "openjpa.hint.isolationLevel"; + private static final int db2ISeriesV5R4AndLater = 5; + private static final String forUpdateOfClause = "FOR UPDATE OF"; + private static final String withRSClause = "WITH RS"; + private static final String withRRClause = "WITH RR"; + private static final String useKeepUpdateLockClause + = "USE AND KEEP UPDATE LOCKS"; + private static final String useKeepExclusiveLockClause + = "USE AND KEEP EXCLUSIVE LOCKS"; + private static final String forReadOnlyClause = "FOR READ ONLY"; + public DB2Dictionary() { platform = "DB2"; validationSQL = "SELECT DISTINCT(CURRENT TIMESTAMP) FROM " @@ -186,18 +187,18 @@ public class DB2Dictionary if (isJDBC3(metaData)) { int maj = metaData.getDatabaseMajorVersion(); int min = metaData.getDatabaseMinorVersion(); - + // Determine the type of DB2 database if (isDB2ISeriesV5R3AndEarlier(metaData)) - db2ServerType =db2ISeriesV5R3AndEarlier; + db2ServerType = db2ISeriesV5R3AndEarlier; else if (isDB2UDBV81OrEarlier(metaData,maj,min)) - db2ServerType =db2UDBV81OrEarlier; + db2ServerType = db2UDBV81OrEarlier; else if (isDB2ZOSV8x(metaData,maj)) - db2ServerType =db2ZOSV8x; + db2ServerType = db2ZOSV8x; else if (isDB2UDBV82AndLater(metaData,maj,min)) - db2ServerType=db2UDBV82AndLater; + db2ServerType = db2UDBV82AndLater; else if (isDB2ISeriesV5R4AndLater(metaData)) - db2ServerType=db2ISeriesV5R4AndLater; + db2ServerType = db2ISeriesV5R4AndLater; if (maj >= 9 || (maj == 8 && min >= 2)) { supportsLockingWithMultipleTables = true; @@ -225,128 +226,126 @@ 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 */ - public String getForUpdateClause(JDBCFetchConfiguration fetch, boolean forUpdate) { - String isolationLevel = null; - boolean updateClause; - DatabaseMetaData metaData = null; + protected String getForUpdateClause(JDBCFetchConfiguration fetch, + boolean forUpdate) { + int isolationLevel; StringBuffer forUpdateString = new StringBuffer(); try { - // Determine the update clause/isolationLevel the hint - // overrides the persistence.xml value - if (fetch != null && fetch.getHint(UPDATE_HINT) - !=null ) - updateClause = ((Boolean)fetch. - getHint(UPDATE_HINT)).booleanValue(); - else - updateClause = forUpdate; - if (fetch != null &&fetch.getHint(ISOLATION_HINT) - !=null ) - isolationLevel = (String)fetch. - getHint(ISOLATION_HINT); - else - isolationLevel = conf.getTransactionIsolation(); - if (updateClause == false) + // Determine the isolationLevel; the fetch + // configuration data overrides the persistence.xml value + if (fetch != null && fetch.getIsolationLevel() != -1) + isolationLevel = fetch.getIsolationLevel(); + else + isolationLevel = conf.getTransactionIsolationConstant(); + + if (!forUpdate) { // This sql is not for update so add FOR Read Only clause forUpdateString.append(" ").append(forReadOnlyClause) .append(" "); - else if (updateClause == true){ + } else { - switch(db2ServerType){ + switch(db2ServerType) { case db2ISeriesV5R3AndEarlier: - case db2UDBV81OrEarlier: - if (isolationLevel.equals("read-uncommitted")) + case db2UDBV81OrEarlier: + if (isolationLevel == + Connection.TRANSACTION_READ_UNCOMMITTED) { forUpdateString.append(" ").append(withRSClause) - .append(" ").append(forUpdateOfClause).append(" "); - else + .append(" ").append(forUpdateOfClause).append(" "); + } else { forUpdateString.append(" ").append(forUpdateOfClause) - .append(" "); - break; + .append(" "); + } + break; case db2ZOSV8x: - case db2UDBV82AndLater: - if (isolationLevel.equals("serializable")) + case db2UDBV82AndLater: + if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) { forUpdateString.append(" ").append(withRRClause) - .append(" ").append(useKeepUpdateLockClause) - .append(" "); - else + .append(" ").append(useKeepUpdateLockClause) + .append(" "); + } else { forUpdateString.append(" ").append(withRSClause) - .append(" ").append(useKeepUpdateLockClause) - .append(" "); + .append(" ").append(useKeepUpdateLockClause) + .append(" "); + } break; case db2ISeriesV5R4AndLater: - if (isolationLevel.equals("serializable")) + if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) { forUpdateString.append(" ").append(withRRClause) - .append(" ").append(useKeepExclusiveLockClause) - .append(" "); - else + .append(" ").append(useKeepExclusiveLockClause) + .append(" "); + } else { forUpdateString.append(" ").append(withRSClause) - .append(" ").append(useKeepExclusiveLockClause) - .append(" "); + .append(" ").append(useKeepExclusiveLockClause) + .append(" "); + } + break; } } - } + } catch (Exception e) { if (log.isTraceEnabled()) log.error(e.toString(),e); } return forUpdateString.toString(); - } - + } + public boolean isDB2UDBV82AndLater(DatabaseMetaData metadata, int maj, int min) throws SQLException { boolean match = false; - if (metadata.getDatabaseProductVersion().indexOf("SQL") != -1 - && ((maj ==8 && min >=2) ||(maj >=8))) - match = true; + if (metadata.getDatabaseProductVersion().indexOf("SQL") != -1 + && ((maj == 8 && min >= 2) ||(maj >= 8))) + match = true; return match; } - public boolean isDB2ZOSV8x(DatabaseMetaData metadata,int maj) + public boolean isDB2ZOSV8x(DatabaseMetaData metadata, int maj) throws SQLException { boolean match = false; - if (metadata.getDatabaseProductVersion().indexOf("DSN") != -1 - && maj ==8 ) - match = true; + if (metadata.getDatabaseProductVersion().indexOf("DSN") != -1 + && maj == 8) + match = true; return match; } public boolean isDB2ISeriesV5R3AndEarlier(DatabaseMetaData metadata) throws SQLException { boolean match = false; - if (metadata.getDatabaseProductVersion().indexOf("AS") != -1 + if (metadata.getDatabaseProductVersion().indexOf("AS") != -1 && generateVersionNumber(metadata.getDatabaseProductVersion()) - <= 530 ) - match = true; + <= 530) + match = true; return match; } public boolean isDB2ISeriesV5R4AndLater(DatabaseMetaData metadata) throws SQLException { boolean match = false; - if (metadata.getDatabaseProductVersion().indexOf("AS") != -1 + if (metadata.getDatabaseProductVersion().indexOf("AS") != -1 && generateVersionNumber(metadata.getDatabaseProductVersion()) - >= 540 ) - match = true; + >= 540) + match = true; return match; } - public boolean isDB2UDBV81OrEarlier(DatabaseMetaData metadata,int maj, + public boolean isDB2UDBV81OrEarlier(DatabaseMetaData metadata, int maj, int min) throws SQLException { boolean match = false; if (metadata.getDatabaseProductVersion().indexOf("SQL") != -1 && - ((maj ==8 && min <=1)|| maj <8 )) - match = true; + ((maj == 8 && min <= 1)|| maj < 8)) + match = true; return match; } /** Get the version number for the ISeries - */ + */ protected int generateVersionNumber(String versionString) { String s = versionString.substring(versionString.indexOf('V')); - s = s.toUpperCase(); + s = s.toUpperCase(); int i = -1; StringTokenizer stringtokenizer = new StringTokenizer(s, "VRM", false); if (stringtokenizer.countTokens() == 3) @@ -358,72 +357,17 @@ public class DB2Dictionary } 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, JDBCFetchConfiguration fetch) { - sel.addJoinClassConditions(); - boolean update = forUpdate && sel.getFromSelect() == null; - SQLBuffer select = getSelects(sel, false, update); - SQLBuffer ordering = null; - if (!sel.isAggregate() || sel.getGrouping() != null) - ordering = sel.getOrdering(); - 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); + SQLBuffer buf = super.toSelect(sel, forUpdate, fetch); + + if (sel.getExpectedResultCount() > 0) { + buf.append(" ").append(optimizeClause).append(" ") + .append(String.valueOf(sel.getExpectedResultCount())) + .append(" ").append(rowClause); + } + return buf; } @@ -452,20 +396,20 @@ public class DB2Dictionary getMethod("getSqlWarn", null); Method getSqlErrdMethd = sqlca.getClass(). getMethod("getSqlErrd", null); - exceptionMsg = exceptionMsg.concat( "SQLCA OUTPUT" + + exceptionMsg = exceptionMsg.concat( "SQLCA OUTPUT" + "[Errp=" +getSqlErrpMethd.invoke(sqlca,new Object[]{}) + ", Errd=" + Arrays.toString((int[]) (getSqlErrdMethd.invoke(sqlca, new Object[]{})))); String Warn = new String((char[])getSqlWarnMethd. invoke(sqlca, new Object[]{})); if(Warn.trim().length() != 0) - exceptionMsg = exceptionMsg.concat(", Warn=" +Warn + "]" ); + exceptionMsg = exceptionMsg.concat(", Warn=" +Warn + "]" ); else - exceptionMsg = exceptionMsg.concat( "]" ); + exceptionMsg = exceptionMsg.concat( "]" ); msg = msg.concat(exceptionMsg); return msg; } catch (Throwable t) { return sqle.getMessage(); } } - } \ No newline at end of file + } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java index c2e915a9c..515d6bc43 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java @@ -2145,7 +2145,22 @@ public class DBDictionary SQLBuffer having, SQLBuffer order, boolean distinct, boolean forUpdate, long start, long end) { 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; } /** @@ -2158,10 +2173,10 @@ public class DBDictionary /** * Return the SQL for the given selecting operation. */ - 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) { + 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 forUpdateClause) { SQLBuffer buf = new SQLBuffer(this); buf.append(op); @@ -2190,8 +2205,8 @@ public class DBDictionary if (forUpdate && !simulateLocking) { assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate"); - if (forUpdateClause != null) - buf.append(" ").append(forUpdateClause); + if (this.forUpdateClause != null) + buf.append(" ").append(this.forUpdateClause); } if (range && rangePosition == RANGE_POST_LOCK) appendSelectRange(buf, start, end); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java index 6b88b785c..02a5adee2 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/H2Dictionary.java @@ -159,14 +159,6 @@ public class H2Dictionary extends DBDictionary { 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, String schemaName, String tableName, String columnName, Connection conn) throws SQLException { @@ -175,18 +167,6 @@ public class H2Dictionary extends DBDictionary { 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) { if (end != Long.MAX_VALUE) buf.append(" LIMIT ").appendValue(end - start); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java index cdf596110..76d540f1c 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/HSQLDictionary.java @@ -190,16 +190,16 @@ public class HSQLDictionary 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) { + 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 forUpdateClause) { // hsql requires ordering when limit is used if ((start != 0 || end != Long.MAX_VALUE) && (order == null || order.isEmpty())) order = _oneBuffer; 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, diff --git a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties index f104cb773..18b5bffb9 100644 --- a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties +++ b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties @@ -97,3 +97,8 @@ clstable-seq-usage: Usage: \ native-seq-usage: Usage: java org.apache.openjpa.jdbc.kernel.NativeJDBCSeq\n\ \t[-properties/-pThe isolation level for queries issued to the database. This overrides
+ * the persistence-unit-wide openjpa.jdbc.TransactionIsolation
+ * value.
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.
+ * + * @since 0.9.7 + */ + public int getIsolationLevel(); + + /** + *The isolation level for queries issued to the database. This overrides
+ * the persistence-unit-wide openjpa.jdbc.TransactionIsolation
+ * value.
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.
+ * + * @since 0.9.7 + */ + public JDBCFetchPlan setIsolationLevel(int level); } diff --git a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java index 88f1ea6aa..ed179ea42 100644 --- a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java +++ b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/JDBCFetchPlanImpl.java @@ -102,4 +102,13 @@ public class JDBCFetchPlanImpl _fetch.setJoinSyntax(syntax); return this; } + + public int getIsolationLevel() { + return _fetch.getIsolationLevel(); + } + + public JDBCFetchPlan setIsolationLevel(int level) { + _fetch.setIsolationLevel(level); + return this; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestIsolationLevelOverride.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestIsolationLevelOverride.java new file mode 100644 index 000000000..396e4d753 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestIsolationLevelOverride.java @@ -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(); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestSelectForUpdateOverride.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestSelectForUpdateOverride.java new file mode 100644 index 000000000..1c670f453 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestSelectForUpdateOverride.java @@ -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(); + } + } +}