mirror of https://github.com/apache/openjpa.git
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:
parent
e541936463
commit
814637ca6c
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
* <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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -97,3 +97,8 @@ clstable-seq-usage: Usage: \
|
|||
native-seq-usage: Usage: java org.apache.openjpa.jdbc.kernel.NativeJDBCSeq\n\
|
||||
\t[-properties/-p <properties file or resource>]\n\
|
||||
\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}.
|
|
@ -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 \
|
||||
worked around by setting the "SupportsTimestampNanos" DBDictionary \
|
||||
property to "true".
|
||||
isolation-level-config-not-supported: This DBDictionary does not support \
|
||||
customization of isolation levels on a per-query basis. DBDictionary: {0}.
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
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;
|
||||
|
@ -114,4 +116,38 @@ public interface JDBCFetchPlan
|
|||
* @see JoinSyntaxes
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue