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 c365709bc..a65b490d4 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 @@ -50,12 +50,12 @@ public class DB2Dictionary public String optimizeClause = "optimize for"; public String rowClause = "row"; protected int db2ServerType = 0; - protected static final int db2ISeriesV5R3OrEarlier = 1; - protected static final int db2UDBV81OrEarlier = 2; - protected static final int db2ZOSV8xOrLater = 3; - protected static final int db2UDBV82OrLater = 4; - protected static final int db2ISeriesV5R4OrLater = 5; - private static final String forUpdateOfClause = "FOR UPDATE OF"; + public static final int db2ISeriesV5R3OrEarlier = 1; + public static final int db2UDBV81OrEarlier = 2; + public static final int db2ZOSV8xOrLater = 3; + public static final int db2UDBV82OrLater = 4; + public static final int db2ISeriesV5R4OrLater = 5; + private static final String forUpdate = "FOR UPDATE"; private static final String withRSClause = "WITH RS"; private static final String withRRClause = "WITH RR"; private static final String useKeepUpdateLockClause @@ -313,9 +313,12 @@ public class DB2Dictionary * updateClause and isolationLevel hints */ protected String getForUpdateClause(JDBCFetchConfiguration fetch, - boolean forUpdate) { + boolean isForUpdate) { int isolationLevel; - StringBuffer forUpdateString = new StringBuffer(); + // For db2UDBV81OrEarlier and db2ISeriesV5R3OrEarlier: + // "optimize for" clause appears before "for update" clause. + StringBuffer forUpdateString = new StringBuffer( + getOptimizeClause(fetch)); try { // Determine the isolationLevel; the fetch // configuration data overrides the persistence.xml value @@ -324,16 +327,15 @@ public class DB2Dictionary else isolationLevel = conf.getTransactionIsolationConstant(); - if (forUpdate) { + if (isForUpdate) { switch(db2ServerType) { case db2ISeriesV5R3OrEarlier: case db2UDBV81OrEarlier: - if (isolationLevel == - Connection.TRANSACTION_READ_UNCOMMITTED) { - forUpdateString.append(" ").append(withRSClause) - .append(" ").append(forUpdateOfClause); - } else - forUpdateString.append(" ").append(forUpdateOfClause); + if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE) + forUpdateString.append(" ").append(forUpdateClause); + else + forUpdateString.append(" ").append(forUpdate) + .append(" ").append(withRSClause); break; case db2ZOSV8xOrLater: case db2UDBV82OrLater: @@ -368,7 +370,7 @@ public class DB2Dictionary return forUpdateString.toString(); } - public boolean isDB2UDBV82OrLater() throws SQLException { + public boolean isDB2UDBV82OrLater() { boolean match = false; if ((databaseProductVersion.indexOf("SQL") != -1 || databaseProductName.indexOf("DB2/") != -1) @@ -377,8 +379,7 @@ public class DB2Dictionary return match; } - public boolean isDB2ZOSV8xOrLater() - throws SQLException { + public boolean isDB2ZOSV8xOrLater() { boolean match = false; if ((databaseProductVersion.indexOf("DSN") != -1 || databaseProductName.indexOf("DB2/") == -1) @@ -387,8 +388,7 @@ public class DB2Dictionary return match; } - public boolean isDB2ISeriesV5R3OrEarlier() - throws SQLException { + public boolean isDB2ISeriesV5R3OrEarlier() { boolean match = false; if (databaseProductName.indexOf("AS") != -1 && ((maj == 5 && min <=3) || maj < 5)) @@ -396,8 +396,7 @@ public class DB2Dictionary return match; } - public boolean isDB2ISeriesV5R4OrLater() - throws SQLException { + public boolean isDB2ISeriesV5R4OrLater() { boolean match = false; if (databaseProductName.indexOf("AS") != -1 && (maj >=6 || (maj == 5 && min >=4))) @@ -405,7 +404,7 @@ public class DB2Dictionary return match; } - public boolean isDB2UDBV81OrEarlier() throws SQLException { + public boolean isDB2UDBV81OrEarlier() { boolean match = false; if ((databaseProductVersion.indexOf("SQL") != -1 || databaseProductName.indexOf("DB2/") != -1) && @@ -465,17 +464,16 @@ public class DB2Dictionary } } - public SQLBuffer toSelect(Select sel, boolean forUpdate, - JDBCFetchConfiguration fetch) { - SQLBuffer buf = super.toSelect(sel, forUpdate, fetch); - - if (sel.getExpectedResultCount() > 0) { + protected String getOptimizeClause(JDBCFetchConfiguration fetch) { + if (sel != null && sel.getExpectedResultCount() > 0) { + StringBuffer buf = new StringBuffer(); buf.append(" ").append(optimizeClause).append(" ") .append(String.valueOf(sel.getExpectedResultCount())) .append(" ").append(rowClause); + return buf.toString(); } - return buf; + return ""; } public OpenJPAException newStoreException(String msg, SQLException[] causes, 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 a971e594f..3ac0f2610 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 @@ -325,6 +325,7 @@ public class DBDictionary private Method _setBytes = null; private Method _setString = null; private Method _setCharStream = null; + protected transient Select sel = null; public DBDictionary() { fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{ @@ -1988,6 +1989,7 @@ public class DBDictionary */ public SQLBuffer toSelect(Select sel, boolean forUpdate, JDBCFetchConfiguration fetch) { + this.sel = sel; sel.addJoinClassConditions(); boolean update = forUpdate && sel.getFromSelect() == null; SQLBuffer select = getSelects(sel, false, update); @@ -2198,11 +2200,11 @@ public class DBDictionary * updateClause and isolationLevel hints */ protected String getForUpdateClause(JDBCFetchConfiguration fetch, - boolean forUpdate) { + boolean isForUpdate) { if (fetch != null && fetch.getIsolation() != -1) { throw new InvalidStateException(_loc.get( "isolation-level-config-not-supported", getClass().getName())); - } else if (forUpdate && !simulateLocking) { + } else if (isForUpdate && !simulateLocking) { assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate"); return forUpdateClause; } else { 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 index 341480b09..41c0298fb 100644 --- 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 @@ -81,37 +81,23 @@ public class TestIsolationLevelOverride q.getResultList(); if (dict instanceof DB2Dictionary) { - if ((((DB2Dictionary) dict).getDb2ServerType() == 1) - || (((DB2Dictionary) dict).getDb2ServerType()== 2)) { + int db2server = ((DB2Dictionary) dict).getDb2ServerType(); + if (db2server == DB2Dictionary.db2ISeriesV5R3OrEarlier + || db2server == DB2Dictionary.db2UDBV81OrEarlier) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," - + " t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField, " - + "t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE \\(t0.intField = \\?\\) " - + " FOR UPDATE OF"); + assertContainsSQL(" FOR UPDATE"); } // it is DB2 v82 or later - else if ((((DB2Dictionary) dict).getDb2ServerType() == 3) - || (((DB2Dictionary) dict).getDb2ServerType() == 4)) { + else if (db2server == DB2Dictionary.db2ZOSV8xOrLater + || db2server == DB2Dictionary.db2UDBV82OrLater) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," - + " t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField, " - + "t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE \\(t0.intField = \\?\\) " - + " FOR READ ONLY WITH RR USE AND KEEP " + assertContainsSQL(" FOR READ ONLY WITH RR USE AND KEEP " + "UPDATE LOCKS"); } - else if (((DB2Dictionary) dict).getDb2ServerType() == 5) { + else if (db2server == DB2Dictionary.db2ISeriesV5R4OrLater) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," - + " t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField, " - + "t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE \\(t0.intField = \\?\\) " - + " FOR READ ONLY WITH RR USE AND KEEP EXCLUSIVE " - + "LOCKS"); + assertContainsSQL(" FOR READ ONLY WITH RR USE AND KEEP" + + " EXCLUSIVE LOCKS"); } else { fail("OpenJPA currently only supports " @@ -124,37 +110,25 @@ public class TestIsolationLevelOverride .setIsolation(IsolationLevel.SERIALIZABLE); em.find(AllFieldTypes.class, 0); if (dict instanceof DB2Dictionary ) { - if ((((DB2Dictionary) dict).getDb2ServerType() == 1) - || (((DB2Dictionary) dict).getDb2ServerType()== 2)) { + int db2server = ((DB2Dictionary) dict).getDb2ServerType(); + if (db2server == DB2Dictionary.db2ISeriesV5R3OrEarlier + || db2server == DB2Dictionary.db2UDBV81OrEarlier) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.booleanField, t0.byteField, " - + "t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField," - + " t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE t0.id = \\? " - + " FOR UPDATE OF optimize for 1 row"); + assertContainsSQL(" optimize for 1 row FOR UPDATE"); } // it is DB2 v82 or later - else if ((((DB2Dictionary) dict).getDb2ServerType() == 3) - || (((DB2Dictionary) dict).getDb2ServerType() == 4)) { + else if (db2server == DB2Dictionary.db2ZOSV8xOrLater + || db2server == DB2Dictionary.db2UDBV82OrLater) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.booleanField, t0.byteField, " - + "t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField," - + " t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE t0.id = \\? " + assertContainsSQL(" optimize for 1 row" + " FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS" - + " optimize for 1 row"); + ); } - else if (((DB2Dictionary) dict).getDb2ServerType() == 5) { + else if (db2server == DB2Dictionary.db2ISeriesV5R4OrLater) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.booleanField, t0.byteField, " - + "t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField," - + " t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE t0.id = \\? " + assertContainsSQL(" optimize for 1 row" + " FOR READ ONLY WITH RR USE AND KEEP EXCLUSIVE" - + " LOCKS optimize for 1 row"); + + " LOCKS"); } else { fail("OpenJPA currently only supports per-query" diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestOptimizeForClause.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestOptimizeForClause.java index 4b41d585e..c8ba92349 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestOptimizeForClause.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestOptimizeForClause.java @@ -81,22 +81,14 @@ public class TestOptimizeForClause } if (dict instanceof DB2Dictionary) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," + - " t0.charField, t0.dateField, t0.doubleField, " + - "t0.floatField, t0.intField, t0.longField, " + - "t0.shortField, t0.stringField FROM AllFieldTypes " + - "t0 WHERE \\(t0.intField = \\?\\) optimize for 8 row"); + assertContainsSQL(" optimize for 8 row"); } } else { em.find(AllFieldTypes.class, 0); if (dict instanceof DB2Dictionary ) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.booleanField, t0.byteField, " + - "t0.charField, t0.dateField, t0.doubleField, " + - "t0.floatField, t0.intField, t0.longField, " + - "t0.shortField, t0.stringField FROM AllFieldTypes" + - " t0 WHERE t0.id = \\? optimize for 1 row"); + assertContainsSQL(" optimize for 1 row"); } } 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 index 2c1bb4e68..8195982e1 100644 --- 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 @@ -33,7 +33,14 @@ public class TestSelectForUpdateOverride extends SQLListenerTestCase { public void setUp() { - setUp(AllFieldTypes.class, + setUp( +// "openjpa.ConnectionDriverName", +// "org.apache.commons.dbcp.BasicDataSource", + + "openjpa.ConnectionProperties", + "DriverClassName=com.ibm.db2.jcc.DB2Driver,Url=jdbc:db2:demodb" + ,"openjpa.jdbc.SynchronizeMappings", + AllFieldTypes.class, "openjpa.Optimistic", "false", "openjpa.LockManager", "pessimistic", "openjpa.ReadLockLevel", "none"); @@ -57,37 +64,24 @@ public class TestSelectForUpdateOverride em.find(AllFieldTypes.class, 0); assertEquals(1, sql.size()); if (dict instanceof DB2Dictionary) { - if ((((DB2Dictionary) dict).getDb2ServerType() == 1) - || (((DB2Dictionary) dict).getDb2ServerType()== 2)) { + int db2server = ((DB2Dictionary) dict).getDb2ServerType(); + if (db2server == DB2Dictionary.db2ISeriesV5R3OrEarlier + || db2server == DB2Dictionary.db2UDBV81OrEarlier) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.booleanField, t0.byteField, " - + "t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField," - + " t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE t0.id = \\? " - + " FOR UPDATE OF optimize for 1 row"); + assertContainsSQL(" optimize for 1 row FOR UPDATE"); } // it is DB2 v82 or later - else if ((((DB2Dictionary) dict).getDb2ServerType() == 3) - || (((DB2Dictionary) dict).getDb2ServerType() == 4)) { + else if (db2server == DB2Dictionary.db2ZOSV8xOrLater + || db2server == DB2Dictionary.db2UDBV82OrLater) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.booleanField, t0.byteField, " - + "t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField," - + " t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE t0.id = \\? " - + " FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS" - + " optimize for 1 row"); + assertContainsSQL(" optimize for 1 row" + + " FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS"); } - else if (((DB2Dictionary) dict).getDb2ServerType() == 5) { + else if (db2server == DB2Dictionary.db2ISeriesV5R4OrLater) { assertEquals(1, sql.size()); - assertSQL("SELECT t0.booleanField, t0.byteField, " - + "t0.charField, t0.dateField, t0.doubleField," - + " t0.floatField, t0.intField, t0.longField," - + " t0.shortField, t0.stringField FROM " - + "AllFieldTypes t0 WHERE t0.id = \\? " + assertContainsSQL(" optimize for 1 row" + " FOR READ ONLY WITH RS USE AND KEEP EXCLUSIVE LOCKS" - + " optimize for 1 row"); + ); } else { fail("OpenJPA currently only supports per-query isolation " diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java index 17debdb34..f091b7ec5 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java @@ -78,6 +78,21 @@ public abstract class SQLListenerTestCase + " should not have been executed in SQL statements: " + sql); } + /** + * Confirm that the executed SQL String contains the specified sqlExp. + * + * @param sqlExp the SQL expression. E.g., "SELECT BADCOLUMN .*" + */ + public void assertContainsSQL(String sqlExp) { + for (String statement : sql) { + if (statement.contains(sqlExp)) + return; + } + + fail("Expected regular expression <" + sqlExp + "> to be" + + " contained in SQL statements: " + sql); + } + public class Listener extends AbstractJDBCListener {