mirror of https://github.com/apache/openjpa.git
OPENJPA-1402 - Improve concurrency by not locking relation field whenever appropriate for mixed lock manager.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@884744 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a1221ea36d
commit
cbbe382ecd
|
@ -46,4 +46,9 @@ public interface JDBCLockManager
|
|||
* produced by a FOR UPDATE select.
|
||||
*/
|
||||
public void loadedForUpdate(OpenJPAStateManager sm);
|
||||
|
||||
/**
|
||||
* Return true if locking is not desired for relation fields.
|
||||
*/
|
||||
public boolean skipRelationFieldLock();
|
||||
}
|
||||
|
|
|
@ -128,4 +128,8 @@ public class MixedLockManager extends PessimisticLockManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean skipRelationFieldLock() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.util.Map;
|
|||
import org.apache.openjpa.jdbc.kernel.JDBCStore;
|
||||
import org.apache.openjpa.jdbc.sql.Result;
|
||||
import org.apache.openjpa.jdbc.sql.Select;
|
||||
import org.apache.openjpa.jdbc.schema.Column;
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.StoreManager;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.math.BigDecimal;
|
|||
import java.sql.SQLException;
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
||||
import org.apache.openjpa.jdbc.kernel.JDBCStore;
|
||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||
import org.apache.openjpa.jdbc.meta.VersionMappingInfo;
|
||||
|
@ -34,6 +35,7 @@ import org.apache.openjpa.jdbc.sql.Result;
|
|||
import org.apache.openjpa.jdbc.sql.Row;
|
||||
import org.apache.openjpa.jdbc.sql.RowManager;
|
||||
import org.apache.openjpa.jdbc.sql.Select;
|
||||
import org.apache.openjpa.kernel.MixedLockLevels;
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.StoreManager;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
|
@ -277,7 +279,13 @@ public abstract class ColumnVersionStrategy
|
|||
sel.select(cols);
|
||||
sel.wherePrimaryKey(sm.getObjectId(), vers.getClassMapping(), store);
|
||||
|
||||
Result res = sel.execute(store, null);
|
||||
// No need to lock version field (i.e. optimistic), except when version update is required (e.g. refresh)
|
||||
JDBCFetchConfiguration fetch = store.getFetchConfiguration();
|
||||
if (!updateVersion && fetch.getReadLockLevel() >= MixedLockLevels.LOCK_PESSIMISTIC_READ) {
|
||||
fetch = (JDBCFetchConfiguration) fetch.clone();
|
||||
fetch.setReadLockLevel(MixedLockLevels.LOCK_NONE);
|
||||
}
|
||||
Result res = sel.execute(store, fetch);
|
||||
try {
|
||||
if (!res.next())
|
||||
return false;
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.openjpa.enhance.PersistenceCapable;
|
|||
import org.apache.openjpa.enhance.ReflectingPersistenceCapable;
|
||||
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
|
||||
import org.apache.openjpa.jdbc.kernel.JDBCStore;
|
||||
import org.apache.openjpa.jdbc.kernel.MixedLockManager;
|
||||
import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||
import org.apache.openjpa.jdbc.meta.Embeddable;
|
||||
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
||||
|
@ -53,6 +54,7 @@ import org.apache.openjpa.jdbc.sql.SQLBuffer;
|
|||
import org.apache.openjpa.jdbc.sql.Select;
|
||||
import org.apache.openjpa.jdbc.sql.SelectExecutor;
|
||||
import org.apache.openjpa.jdbc.sql.Union;
|
||||
import org.apache.openjpa.kernel.LockManager;
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
|
@ -65,6 +67,7 @@ import org.apache.openjpa.util.InternalException;
|
|||
import org.apache.openjpa.util.MetaDataException;
|
||||
import org.apache.openjpa.util.OpenJPAId;
|
||||
import org.apache.openjpa.util.UnsupportedException;
|
||||
|
||||
import serp.util.Numbers;
|
||||
|
||||
/**
|
||||
|
@ -699,8 +702,14 @@ public class RelationFieldStrategy
|
|||
}
|
||||
}
|
||||
|
||||
boolean isLocked = res.isLocking();
|
||||
try {
|
||||
res.setLocking(store.getLockManager().skipRelationFieldLock());
|
||||
sm.storeObject(field.getIndex(), res.load(cls, store, fetch,
|
||||
eagerJoin(res.newJoins(), cls, false)));
|
||||
} finally {
|
||||
res.setLocking(isLocked);
|
||||
}
|
||||
|
||||
// reset mapped by is needed for OneToOne bidirectional relations
|
||||
// having a mapped-by parent to correctly set the parent-child
|
||||
|
|
|
@ -97,6 +97,10 @@ public class MergedResult
|
|||
_res[i].close();
|
||||
}
|
||||
|
||||
public void setLocking(boolean locking) {
|
||||
_res[_idx].setLocking(locking);
|
||||
}
|
||||
|
||||
public boolean isLocking() {
|
||||
return _res[_idx].isLocking();
|
||||
}
|
||||
|
|
|
@ -78,6 +78,11 @@ public interface Result
|
|||
*/
|
||||
public void close();
|
||||
|
||||
/**
|
||||
* Set to true if row locking has been issued for the row.
|
||||
*/
|
||||
public void setLocking(boolean locking);
|
||||
|
||||
/**
|
||||
* If true, then any results loaded from this Result
|
||||
* will be locked in the database.
|
||||
|
|
|
@ -57,7 +57,7 @@ public abstract class AbstractLockManager
|
|||
*/
|
||||
public void lockAll(Collection sms, int level, int timeout,
|
||||
Object context) {
|
||||
for (Iterator itr = sms.iterator(); itr.hasNext();)
|
||||
for (Iterator<?> itr = sms.iterator(); itr.hasNext();)
|
||||
lock((OpenJPAStateManager) itr.next(), level, timeout, context);
|
||||
}
|
||||
|
||||
|
@ -76,8 +76,14 @@ public abstract class AbstractLockManager
|
|||
/**
|
||||
* Does nothing by default.
|
||||
*/
|
||||
public void close ()
|
||||
{
|
||||
public void close () {
|
||||
}
|
||||
|
||||
/**
|
||||
* Default not to skip relation field to maintain PessimisticLockManager semantics.
|
||||
*/
|
||||
public boolean skipRelationFieldLock() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ public abstract class LockScopeTestCase extends SQLListenerTestCase {
|
|||
|
||||
protected final String Any = ".*";
|
||||
protected final String Select = "SELECT.*FROM.*";
|
||||
protected final String SelectVersion = "SELECT.*version.*FROM.*";
|
||||
protected final String Where = ".*WHERE.*";
|
||||
// protected final String Join = ".*(JOIN){1}.*";
|
||||
protected final String NoJoin = "(JOIN){0}";
|
||||
|
@ -155,6 +156,15 @@ public abstract class LockScopeTestCase extends SQLListenerTestCase {
|
|||
assertAllSQLAnyOrder(expected);
|
||||
}
|
||||
|
||||
public void assertLockTestNoSQLs(String... expected) {
|
||||
Log log = getDumpSQLLog();
|
||||
if( log.isTraceEnabled()) {
|
||||
log.trace("\r\n" + toString(sql));
|
||||
return;
|
||||
}
|
||||
assertNoneSQLAnyOrder(expected);
|
||||
}
|
||||
|
||||
protected void logStack(Throwable t) {
|
||||
StringWriter str = new StringWriter();
|
||||
PrintWriter print = new PrintWriter(str);
|
||||
|
|
|
@ -43,8 +43,8 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
, LSE1x1LfJT.class
|
||||
, LSE1x1LfJTLzy.class
|
||||
, LSE1x1Rt.class
|
||||
, "openjpa.LockManager", "mixed",
|
||||
"openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)"
|
||||
, "openjpa.LockManager", "mixed"
|
||||
, "openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)"
|
||||
);
|
||||
commonSetUp(LSE1x1Lf.class
|
||||
, LSE1x1LfLzy.class
|
||||
|
@ -138,12 +138,18 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
case db2:
|
||||
// SELECT t0.version, t0.firstName, t1.id, t1.version, t1.lastName FROM LSE1x1Lf t0
|
||||
// LEFT OUTER JOIN LSE1x1Rt t1 ON t0.UNIRIGHT_ID = t1.id WHERE t0.id = ?
|
||||
// optimize for 1 row FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1111202]
|
||||
// optimize for 1 row FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS
|
||||
// [params=(int) 1111202]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ?
|
||||
// FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1121202]
|
||||
// -FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS- [params=(int) 1121202]
|
||||
// SELECT t0.version FROM LSE1x1Lf t0 WHERE t0.id = ?
|
||||
// FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1111202]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock);
|
||||
// -FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS- [params=(int) 1111202]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock,
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
case oracle: // TODO: if jpa2, DO NOT lock LSE1x1RT using "FOR UPDATE OF col"
|
||||
// SELECT t0.version, t0.firstName, t1.id, t1.version, t1.lastName
|
||||
|
@ -154,20 +160,25 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
assertLockTestSQLs(Select + tableLfName + Any + tableRtName + Where + "\\(\\+\\).*"
|
||||
+ ForUpdate);
|
||||
break;
|
||||
case derby: //TODO: **Non-atomic lock.
|
||||
case derby: //-TODO: **Non-atomic lock.
|
||||
// The database is unable to lock this query. Each object matching the query will be
|
||||
// locked individually after it is loaded; however, it is technically possible that
|
||||
// another transaction could modify the data before the lock is obtained.
|
||||
// SELECT t0.version, t0.firstName, t1.id, t1.version, t1.lastName FROM LSE1x1Lf t0
|
||||
// LEFT OUTER JOIN LSE1x1Rt t1 ON t0.UNIRIGHT_ID = t1.id WHERE t0.id = ?
|
||||
// [params=(int) 1111202]
|
||||
// SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1121202]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1121202]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? -FOR UPDATE WITH RR-
|
||||
// [params=(int) 1121202]
|
||||
// SELECT t0.id FROM LSE1x1Lf t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1111202]
|
||||
// SELECT t0.version FROM LSE1x1Lf t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1111202]
|
||||
// SELECT t0.version FROM LSE1x1Lf t0 WHERE t0.id = ? -FOR UPDATE WITH RR-
|
||||
// [params=(int) 1111202]
|
||||
assertLockTestSQLs(Select + joinTables + Where + NoForUpdate,
|
||||
Select + NoJoin + Any + tableLfName + Any + NoJoin + Where + ForUpdate,
|
||||
Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
//-SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1121202]-
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
@ -184,7 +195,12 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS [params=(String) firstName%1111201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1121201]
|
||||
// SELECT t0.version FROM LSE1x1Lf t0 WHERE t0.id = ? [params=(int) 1111201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock);
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock,
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
case oracle: // TODO: if jpa2, DO NOT lock LSE1x1RT using "FOR UPDATE OF col"
|
||||
// SELECT t0.id, t0.version, t0.firstName, t1.id, t1.version, t1.lastName
|
||||
|
@ -196,20 +212,23 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
assertLockTestSQLs(Select + tableLfName + Any + tableRtName + Where + "\\(\\+\\).*"
|
||||
+ ForUpdate);
|
||||
break;
|
||||
case derby: //TODO: **Non-atomic lock.
|
||||
case derby: //-TODO: **Non-atomic lock.
|
||||
// The database is unable to lock this query. Each object matching the query will be
|
||||
// locked individually after it is loaded; however, it is technically possible that
|
||||
// another transaction could modify the data before the lock is obtained.
|
||||
// SELECT t0.id, t0.version, t0.firstName, t1.id, t1.version, t1.lastName FROM LSE1x1Lf t0
|
||||
// LEFT OUTER JOIN LSE1x1Rt t1 ON t0.UNIRIGHT_ID = t1.id
|
||||
// WHERE (t0.firstName LIKE ? ESCAPE '\') [params=(String) firstName%1111201]
|
||||
// SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1121201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1121201]
|
||||
// SELECT t0.id FROM LSE1x1Lf t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1111201]
|
||||
// SELECT t0.version FROM LSE1x1Lf t0 WHERE t0.id = ? [params=(int) 1111201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + NoForUpdate,
|
||||
Select + NoJoin + Any + tableLfName + Any + NoJoin + Where + ForUpdate,
|
||||
Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
//-SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1121201]-
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
@ -250,7 +269,12 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(String) firstName%1111201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1121201]
|
||||
// SELECT t0.version FROM LSE1x1Lf t0 WHERE t0.id = ? [params=(int) 1111201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock);
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock,
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
case oracle: // TODO: if jpa2, DO NOT lock LSE1x1RT using "FOR UPDATE OF col"
|
||||
// SELECT t0.id, t0.version, t0.firstName, t1.id, t1.version, t1.lastName
|
||||
|
@ -262,20 +286,23 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
assertLockTestSQLs(Select + tableLfName + Any + tableRtName + Where + "\\(\\+\\).*"
|
||||
+ ForUpdate);
|
||||
break;
|
||||
case derby: //TODO: **Non-atomic lock.
|
||||
case derby: //-TODO: **Non-atomic lock.
|
||||
// The database is unable to lock this query. Each object matching the query will be
|
||||
// locked individually after it is loaded; however, it is technically possible that
|
||||
// another transaction could modify the data before the lock is obtained.
|
||||
// SELECT t0.id, t0.version, t0.firstName, t1.id, t1.version, t1.lastName FROM LSE1x1Lf t0
|
||||
// LEFT OUTER JOIN LSE1x1Rt t1 ON t0.UNIRIGHT_ID = t1.id
|
||||
// WHERE (t0.firstName LIKE ? ESCAPE '\') [params=(String) firstName%1111201]
|
||||
// SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1121201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1121201]
|
||||
// SELECT t0.id FROM LSE1x1Lf t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1111201]
|
||||
// SELECT t0.version FROM LSE1x1Lf t0 WHERE t0.id = ? [params=(int) 1111201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + NoForUpdate,
|
||||
Select + NoJoin + Any + tableLfName + Any + NoJoin + Where + ForUpdate,
|
||||
Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
//-SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1121201]-
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
@ -579,10 +606,18 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// SELECT t0.version, t0.firstName, t2.id, t2.version, t2.lastName FROM LSE1x1LfJT t0
|
||||
// INNER JOIN Uni1x1LfJT_Uni1x1RT t1 ON t0.id = t1.LSE1X1LFJT_ID
|
||||
// LEFT OUTER JOIN LSE1x1Rt t2 ON t1.UNIRIGHTJT_ID = t2.id WHERE t0.id = ?
|
||||
// optimize for 1 row FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1112202]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1122202]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ? FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1112202]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock);
|
||||
// optimize for 1 row FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS
|
||||
// [params=(int) 1112202]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ?
|
||||
// FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1122202]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ?
|
||||
// FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(int) 1112202]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock,
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
case oracle: // TODO: If jpa2, DO NOT lock LSE1x1RT using "FOR UDPATE OF col"
|
||||
// SELECT t0.version, t0.firstName, t2.id, t2.version, t2.lastName
|
||||
|
@ -594,7 +629,7 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
assertLockTestSQLs(Select + tableLfName + Any + tableJTName + Any + tableRtName + Where
|
||||
+ "\\(\\+\\).*" + ForUpdate);
|
||||
break;
|
||||
case derby: //TODO: **Non-atomic lock, if jpa2/extended scope, LOCK Uni1x1LfJT_Uni1x1RT
|
||||
case derby: //-TODO: **Non-atomic lock, if jpa2/extended scope, LOCK Uni1x1LfJT_Uni1x1RT
|
||||
// DO NOT lock LSE1x1Rt
|
||||
// The database is unable to lock this query. Each object matching the query will be
|
||||
// locked individually after it is loaded; however, it is technically possible that
|
||||
|
@ -603,13 +638,18 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// INNER JOIN Uni1x1LfJT_Uni1x1RT t1 ON t0.id = t1.LSE1X1LFJT_ID
|
||||
// LEFT OUTER JOIN LSE1x1Rt t2 ON t1.UNIRIGHTJT_ID = t2.id WHERE t0.id = ?
|
||||
// [params=(int) 1112202]
|
||||
// SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1122202]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1122202]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? -FOR UPDATE WITH RR-
|
||||
// [params=(int) 1122202]
|
||||
// SELECT t0.id FROM LSE1x1LfJT t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1112202]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1112202]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ? -FOR UPDATE WITH RR-
|
||||
// [params=(int) 1112202]
|
||||
assertLockTestSQLs(Select + joinTables + Where + NoForUpdate,
|
||||
Select + NoJoin + Any + tableLfName + Any + NoJoin + Where + ForUpdate,
|
||||
Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
//-SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1122202]-
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
@ -627,7 +667,12 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS [params=(String) firstName%1112201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1122201]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ? [params=(int) 1112201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock);
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock,
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
case oracle: // TODO: If jpa2, DO NOT lock LSE1x1RT using "FOR UDPATE OF col"
|
||||
// SELECT t0.id, t0.version, t0.firstName, t2.id, t2.version, t2.lastName
|
||||
|
@ -639,7 +684,7 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
assertLockTestSQLs(Select + tableLfName + Any + tableJTName + Any + tableRtName + Where
|
||||
+ "\\(\\+\\).*" + ForUpdate);
|
||||
break;
|
||||
case derby: //TODO: **Non-atomic lock, if jpa2/extended scope, LOCK Uni1x1LfJT_Uni1x1RT
|
||||
case derby: //-TODO: **Non-atomic lock, if jpa2/extended scope, LOCK Uni1x1LfJT_Uni1x1RT
|
||||
// DO NOT lock LSE1x1Rt
|
||||
// The database is unable to lock this query. Each object matching the query will be
|
||||
// locked individually after it is loaded; however, it is technically possible that
|
||||
|
@ -648,13 +693,16 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// INNER JOIN Uni1x1LfJT_Uni1x1RT t1 ON t0.id = t1.LSE1X1LFJT_ID
|
||||
// LEFT OUTER JOIN LSE1x1Rt t2 ON t1.UNIRIGHTJT_ID = t2.id
|
||||
// WHERE (t0.firstName LIKE ? ESCAPE '\') [params=(String) firstName%1112201]
|
||||
// SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1122201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1122201]
|
||||
// SELECT t0.id FROM LSE1x1LfJT t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1112201]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ? [params=(int) 1112201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + NoForUpdate,
|
||||
Select + NoJoin + Any + tableLfName + Any + NoJoin + Where + ForUpdate,
|
||||
Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
//-SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1122201]-
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
@ -699,7 +747,12 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS [params=(String) firstName%1112201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1122201]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ? [params=(int) 1112201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock);
|
||||
assertLockTestSQLs(Select + joinTables + Where + DB2Lock,
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
case oracle: // TODO: If jpa2, DO NOT lock LSE1x1RT using "FOR UDPATE OF col"
|
||||
// SELECT t0.id, t0.version, t0.firstName, t2.id, t2.version, t2.lastName
|
||||
|
@ -711,7 +764,7 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
assertLockTestSQLs(Select + tableLfName + Any + tableJTName + Any + tableRtName + Where
|
||||
+ "\\(\\+\\).*" + ForUpdate);
|
||||
break;
|
||||
case derby: //TODO: **Non-atomic lock, if jpa2/extended scope, LOCK Uni1x1LfJT_Uni1x1RT
|
||||
case derby: //-TODO: **Non-atomic lock, if jpa2/extended scope, LOCK Uni1x1LfJT_Uni1x1RT
|
||||
// DO NOT lock LSE1x1Rt
|
||||
// The database is unable to lock this query. Each object matching the query will be
|
||||
// locked individually after it is loaded; however, it is technically possible that
|
||||
|
@ -720,13 +773,16 @@ public class Test1x1LockScope extends LockScopeTestCase {
|
|||
// INNER JOIN Uni1x1LfJT_Uni1x1RT t1 ON t0.id = t1.LSE1X1LFJT_ID
|
||||
// LEFT OUTER JOIN LSE1x1Rt t2 ON t1.UNIRIGHTJT_ID = t2.id
|
||||
// WHERE (t0.firstName LIKE ? ESCAPE '\') [params=(String) firstName%1112201]
|
||||
// SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1122201]
|
||||
// SELECT t0.version FROM LSE1x1Rt t0 WHERE t0.id = ? [params=(int) 1122201]
|
||||
// SELECT t0.id FROM LSE1x1LfJT t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1112201]
|
||||
// SELECT t0.version FROM LSE1x1LfJT t0 WHERE t0.id = ? [params=(int) 1112201]
|
||||
assertLockTestSQLs(Select + joinTables + Where + NoForUpdate,
|
||||
Select + NoJoin + Any + tableLfName + Any + NoJoin + Where + ForUpdate,
|
||||
Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
SelectVersion + NoJoin + Any + tableLfName + Any + NoJoin + Where + NoForUpdate,
|
||||
SelectVersion + NoJoin + Any + tableRtName + Any + NoJoin + Where + NoForUpdate
|
||||
);
|
||||
//-SELECT t0.id FROM LSE1x1Rt t0 WHERE t0.id = ? FOR UPDATE WITH RR [params=(int) 1122201]
|
||||
assertLockTestNoSQLs(Select + NoJoin + Any + tableRtName + Any + NoJoin + Where + ForUpdate
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -574,6 +574,7 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
|||
}
|
||||
if (waitTime < MinThreadWaitInMs / 2)
|
||||
waitTime = MinThreadWaitInMs / 2;
|
||||
log.trace(">> Started wait for " + waitTime + " ms");
|
||||
if( waitThreadid != 0) {
|
||||
thisThread.wait(waitTime);
|
||||
} else {
|
||||
|
@ -581,6 +582,7 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
|||
wait(waitTime);
|
||||
}
|
||||
}
|
||||
log.trace("<< Ended wait");
|
||||
break;
|
||||
|
||||
case EmployeeNotNull:
|
||||
|
@ -725,7 +727,7 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
|||
new ArrayList<TestThread>(threads);
|
||||
while (proceedThread.size() > 0
|
||||
&& System.currentTimeMillis() < endTime) {
|
||||
for (Thread thread : proceedThread) {
|
||||
for (TestThread thread : proceedThread) {
|
||||
if (thread.isAlive()) {
|
||||
log.trace(thread + " is still alive, wait" +
|
||||
" for 500ms and try again.");
|
||||
|
@ -739,6 +741,9 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
|||
continue;
|
||||
} else {
|
||||
deadThreads++;
|
||||
if(thread.assertError != null){
|
||||
throw thread.assertError;
|
||||
}
|
||||
proceedThread.remove(thread);
|
||||
break;
|
||||
}
|
||||
|
@ -823,8 +828,8 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
|||
logStack(ex);
|
||||
} catch (Error err) {
|
||||
// only remember the first exception caught
|
||||
if (thisThread.throwable == null) {
|
||||
thisThread.throwable = err;
|
||||
if (thisThread.assertError == null) {
|
||||
thisThread.assertError = err;
|
||||
}
|
||||
log.trace("Caught exception and continue: " + err);
|
||||
logStack(err);
|
||||
|
@ -852,11 +857,14 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
|||
}
|
||||
}
|
||||
em.close();
|
||||
Throwable firstThrowable = thisThread.throwable;
|
||||
if (firstThrowable != null) {
|
||||
if( firstThrowable instanceof Error )
|
||||
throw (Error)firstThrowable;
|
||||
if (thisThread.assertError != null) {
|
||||
throw thisThread.assertError;
|
||||
}
|
||||
// Throwable firstThrowable = thisThread.throwable;
|
||||
// if (firstThrowable != null) {
|
||||
// if( firstThrowable instanceof Error )
|
||||
// throw (Error)firstThrowable;
|
||||
// }
|
||||
log.trace("<<<< Sequenced Test: Threads=" + threadToRun + '/'
|
||||
+ numThreads);
|
||||
}
|
||||
|
@ -927,6 +935,7 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
|
|||
private Map<Integer, LockEmployee> employees = null;
|
||||
|
||||
public Throwable throwable = null;
|
||||
public Error assertError = null;
|
||||
public boolean systemRolledback = false;
|
||||
|
||||
public TestThread(int threadToRun, Object[][]... actions) {
|
||||
|
|
|
@ -48,6 +48,7 @@ public class TestMixedLockManagerDeadlock extends SequencedActionsTest {
|
|||
|
||||
/* ======== Find dead lock exception test ============*/
|
||||
public void testFindDeadLockException() {
|
||||
commonFindTest("testFindDeadLockException", LockModeType.READ, null);
|
||||
commonFindTest("testFindDeadLockException", LockModeType.WRITE, dbType == DBType.oracle ? null
|
||||
: ExpectingOptimisticLockExClass);
|
||||
commonFindTest("testFindDeadLockException", LockModeType.PESSIMISTIC_WRITE, ExpectingAnyLockExClass);
|
||||
|
@ -73,6 +74,7 @@ public class TestMixedLockManagerDeadlock extends SequencedActionsTest {
|
|||
|
||||
{Act.WaitAllChildren},
|
||||
{Act.TestException, 1, t1Exceptions},
|
||||
{Act.RollbackTx}
|
||||
};
|
||||
Object[][] thread1 = {
|
||||
{Act.CreateEm},
|
||||
|
@ -81,6 +83,7 @@ public class TestMixedLockManagerDeadlock extends SequencedActionsTest {
|
|||
{Act.Flush},
|
||||
|
||||
{Act.Notify, 0},
|
||||
{Act.Sleep, 1000},
|
||||
{Act.FindWithLock, 1, t1Lock},
|
||||
|
||||
{Act.RollbackTx},
|
||||
|
@ -90,10 +93,11 @@ public class TestMixedLockManagerDeadlock extends SequencedActionsTest {
|
|||
|
||||
/* ======== named query dead lock exception test ============*/
|
||||
public void testNamedQueryDeadLockException() {
|
||||
commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.READ, null);
|
||||
commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.WRITE, dbType == DBType.oracle ? null
|
||||
: ExpectingOptimisticLockExClass);
|
||||
commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.PESSIMISTIC_FORCE_INCREMENT,
|
||||
ExpectingAnyLockExClass);
|
||||
// commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.PESSIMISTIC_FORCE_INCREMENT,
|
||||
// ExpectingAnyLockExClass);
|
||||
}
|
||||
|
||||
private void commonNamedQueryTest( String testName,
|
||||
|
@ -116,6 +120,8 @@ public class TestMixedLockManagerDeadlock extends SequencedActionsTest {
|
|||
|
||||
{Act.WaitAllChildren},
|
||||
{Act.TestException, 1, t1Exceptions},
|
||||
|
||||
{Act.RollbackTx}
|
||||
};
|
||||
Object[][] thread1 = {
|
||||
{Act.CreateEm},
|
||||
|
@ -124,6 +130,7 @@ public class TestMixedLockManagerDeadlock extends SequencedActionsTest {
|
|||
{Act.Flush},
|
||||
|
||||
{Act.Notify, 0},
|
||||
{Act.Sleep, 1000},
|
||||
{Act.NamedQueryWithLock, "findEmployeeById", 1, t1Lock, "openjpa.hint.IgnorePreparedQuery", true},
|
||||
|
||||
{Act.RollbackTx},
|
||||
|
|
|
@ -38,7 +38,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(Read,Commit/Read,Commit)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(Read,Commit/Read,Rollback)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
|
@ -49,8 +49,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(Read,Commit/Write,Commit)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(Read,Commit/Write,Rollback)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
|
@ -95,7 +94,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(Write,Commit/Read,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(Write,Commit/Read,Rollback)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -106,8 +105,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(Write,Commit/Write,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(Write,Commit/Write,Rollback)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -139,8 +137,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
public void testFindWritePessimisticForceInc() {
|
||||
commonFindTest(
|
||||
"testFind(Write,Commit/PessimisticForceInc,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass,
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null);
|
||||
commonFindTest(
|
||||
"testFind(Write,Commit/PessimisticForceInc,Rollback)",
|
||||
|
@ -153,7 +150,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(PessimisticRead,Commit/Read,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(PessimisticRead,Commit/Read,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
|
@ -164,8 +161,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(PessimisticRead,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(PessimisticRead,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
|
@ -177,12 +173,10 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
"testFind(PessimisticRead,Commit/PessimisticRead,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 0, null);
|
||||
// ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(PessimisticRead,Commit/PessimisticRead,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.RollbackTx, 0, null);
|
||||
// ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
public void testFindPessimisticReadPessimisticWrite() {
|
||||
|
@ -212,7 +206,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(PessimsiticWrite,Commit/Read,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(PessimsiticWrite,Commit/Read,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -223,8 +217,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(PessimsiticWrite,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(PessimsiticWrite,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -269,7 +262,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(PessimsiticForceInc,Commit/Read,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(PessimsiticForceInc,Commit/Read,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
|
@ -280,8 +273,7 @@ public class TestMixedLockManagerFindPermutation extends SequencedActionsTest {
|
|||
commonFindTest(
|
||||
"testFind(PessimsiticForceInc,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonFindTest(
|
||||
"testFind(PessimsiticForceInc,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
|
|
|
@ -38,7 +38,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(Read,Commit/Read,Commit)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(Read,Commit/Read,Rollback)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
|
@ -49,8 +49,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(Read,Commit/Write,Commit)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(Read,Commit/Write,Rollback)",
|
||||
LockModeType.READ, Act.CommitTx, 1, null,
|
||||
|
@ -95,7 +94,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(Write,Commit/Read,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(Write,Commit/Read,Rollback)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -106,8 +105,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(Write,Commit/Write,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(Write,Commit/Write,Rollback)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -139,8 +137,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
public void testLockWritePessimisticForceInc() {
|
||||
commonLockTest(
|
||||
"testLock(Write,Commit/PessimisticForceInc,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass,
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null);
|
||||
commonLockTest(
|
||||
"testLock(Write,Commit/PessimisticForceInc,Rollback)",
|
||||
|
@ -153,7 +150,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/Read,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/Read,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
|
@ -164,8 +161,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
|
@ -176,39 +172,33 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/PessimisticRead,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/PessimisticRead,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_READ, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
public void testLockPessimisticReadPessimisticWrite() {
|
||||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/PessimisticWrite,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/PessimisticWrite,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
public void testLockPessimisticReadPessimisticForceInc() {
|
||||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/PessimisticForceInc,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimisticRead,Commit/PessimisticForceInc,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
/* ======== Thread 1 : Pessimsitic Write Lock ============*/
|
||||
|
@ -216,7 +206,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/Read,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/Read,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -227,8 +217,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
|
@ -239,39 +228,33 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/PessimisticRead,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/PessimisticRead,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_READ, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
public void testLockPessimsiticWritePessimisticWrite() {
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/PessimisticWrite,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/PessimisticWrite,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
public void testLockPessimsiticWritePessimisticForceInc() {
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/PessimisticForceInc,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticWrite,Commit/PessimisticForceInc,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
/* ======== Thread 1 : Pessimsitic Force Increment Lock ============*/
|
||||
|
@ -279,7 +262,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/Read,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/Read,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
|
@ -290,8 +273,7 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
|
@ -302,39 +284,33 @@ public class TestMixedLockManagerLockPermutation extends SequencedActionsTest {
|
|||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/PessimisticRead,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/PessimisticRead,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_READ, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_READ, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
public void testLockPessimsiticForceIncPessimisticWrite() {
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/PessimisticWrite,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/PessimisticWrite,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
public void testLockPessimsiticForceIncPessimisticForceInc() {
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/PessimisticForceInc,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, ExpectingOptimisticLockExClass);
|
||||
commonLockTest(
|
||||
"testLock(PessimsiticForceInc,Commit/PessimisticForceInc,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 1, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 1,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 1, ExpectingOptimisticLockExClass);
|
||||
}
|
||||
|
||||
private void commonLockTest( String testName,
|
||||
|
|
|
@ -107,8 +107,7 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
commonRefreshTest(
|
||||
"testRefresh(Write,Commit/Write,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 2, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 2,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 2, ExpectingOptimisticLockExClass);
|
||||
commonRefreshTest(
|
||||
"testRefresh(Write,Commit/Write,Rollback)",
|
||||
LockModeType.WRITE, Act.CommitTx, 2, null,
|
||||
|
@ -140,8 +139,7 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
public void testRefreshWritePessimisticForceInc() {
|
||||
commonRefreshTest(
|
||||
"testRefresh(Write,Commit/PessimisticForceInc,Commit)",
|
||||
LockModeType.WRITE, Act.CommitTx, 2,
|
||||
ExpectingOptimisticLockExClass,
|
||||
LockModeType.WRITE, Act.CommitTx, 2, ExpectingOptimisticLockExClass,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2, null);
|
||||
commonRefreshTest(
|
||||
"testRefresh(Write,Commit/PessimisticForceInc,Rollback)",
|
||||
|
@ -165,8 +163,7 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
commonRefreshTest(
|
||||
"testRefresh(PessimisticRead,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 2, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 2,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 2, ExpectingOptimisticLockExClass);
|
||||
commonRefreshTest(
|
||||
"testRefresh(PessimisticRead,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_READ, Act.CommitTx, 2, null,
|
||||
|
@ -206,7 +203,7 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 2, null);
|
||||
}
|
||||
|
||||
/* ======== Thread 1 : Pessimsitic Write Lock ============*/
|
||||
/* ======== Thread 1 : Pessimistic Write Lock ============*/
|
||||
public void testRefreshPessimsiticWriteRead() {
|
||||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticWrite,Commit/Read,Commit)",
|
||||
|
@ -222,8 +219,7 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticWrite,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 2, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 2,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 2, ExpectingOptimisticLockExClass);
|
||||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticWrite,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_WRITE, Act.CommitTx, 2, null,
|
||||
|
@ -268,7 +264,7 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticForceInc,Commit/Read,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2, null,
|
||||
LockModeType.READ, Act.CommitTx, 0, ExpectingOptimisticLockExClass);
|
||||
LockModeType.READ, Act.CommitTx, 2, ExpectingOptimisticLockExClass);
|
||||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticForceInc,Commit/Read,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2, null,
|
||||
|
@ -279,8 +275,7 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticForceInc,Commit/Write,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2, null,
|
||||
LockModeType.WRITE, Act.CommitTx, 0,
|
||||
ExpectingOptimisticLockExClass);
|
||||
LockModeType.WRITE, Act.CommitTx, 2, ExpectingOptimisticLockExClass);
|
||||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticForceInc,Commit/Write,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2, null,
|
||||
|
@ -311,13 +306,11 @@ public class TestMixedLockManagerRefreshPermutation
|
|||
|
||||
public void testRefreshPessimsiticForceIncPessimisticForceInc() {
|
||||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticForceInc,Commit/"
|
||||
+ "PessimisticForceInc,Commit)",
|
||||
"testRefresh(PessimsiticForceInc,Commit/PessimisticForceInc,Commit)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 3, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 3, null);
|
||||
commonRefreshTest(
|
||||
"testRefresh(PessimsiticForceInc,Commit/"
|
||||
+ "PessimisticForceInc,Rollback)",
|
||||
"testRefresh(PessimsiticForceInc,Commit/PessimisticForceInc,Rollback)",
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.CommitTx, 2, null,
|
||||
LockModeType.PESSIMISTIC_FORCE_INCREMENT, Act.RollbackTx, 2, null);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ import javax.persistence.PessimisticLockException;
|
|||
import javax.persistence.Query;
|
||||
import javax.persistence.QueryTimeoutException;
|
||||
|
||||
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
||||
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
|
||||
|
||||
/**
|
||||
|
@ -35,9 +37,11 @@ import org.apache.openjpa.persistence.test.SQLListenerTestCase;
|
|||
*/
|
||||
public class TestPessimisticLocks extends SQLListenerTestCase {
|
||||
|
||||
private DBDictionary dict = null;
|
||||
|
||||
public void setUp() {
|
||||
setSupportedDatabases(
|
||||
// org.apache.openjpa.jdbc.sql.DerbyDictionary.class,
|
||||
org.apache.openjpa.jdbc.sql.DerbyDictionary.class,
|
||||
// org.apache.openjpa.jdbc.sql.OracleDictionary.class,
|
||||
org.apache.openjpa.jdbc.sql.DB2Dictionary.class);
|
||||
if (isTestsDisabled()) {
|
||||
|
@ -48,6 +52,8 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
|
|||
String empTable = getMapping(Employee.class).getTable().getFullName();
|
||||
String deptTable = getMapping(Department.class).getTable().getFullName();
|
||||
|
||||
dict= ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance();
|
||||
|
||||
EntityManager em = null;
|
||||
try {
|
||||
em = emf.createEntityManager();
|
||||
|
@ -96,25 +102,27 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
|
|||
}
|
||||
|
||||
/*
|
||||
* Test a find with pessimistic lock after a query with pessimistic lock and expect PessimisticLockException.
|
||||
* Test find with pessimistic lock after a query with pessimistic lock.
|
||||
*/
|
||||
public void testFindWithLockTimeoutAfterQueryWithPessimisticLocks() {
|
||||
public void testFindAfterQueryWithPessimisticLocks() {
|
||||
EntityManager em1 = emf.createEntityManager();
|
||||
EntityManager em2 = emf.createEntityManager();
|
||||
try {
|
||||
em1.getTransaction().begin();
|
||||
|
||||
Query query = em1.createQuery(
|
||||
"select e from Employee e where e.id < 10 order by e.id").setFirstResult(1);
|
||||
"select e from Employee e where e.id < 10").setFirstResult(1);
|
||||
// Lock all selected Employees, skip the first one, i.e should lock Employee(2)
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
List<Employee> q = query.getResultList();
|
||||
assertEquals("Expected 1 element with emplyee id=2", q.size(), 1);
|
||||
assertEquals("Test Employee first name = 'first.2'", q.get(0).getFirstName(), "first.2");
|
||||
assertTrue("Test Employee first name = 'first.2'", q.get(0).getFirstName().equals("first.1")
|
||||
|| q.get(0).getFirstName().equals("first.2"));
|
||||
|
||||
em2.getTransaction().begin();
|
||||
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
// find Employee(2) with a lock, should block and expected a PessimisticLockException
|
||||
em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
|
||||
fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
|
||||
} catch (QueryTimeoutException e) {
|
||||
|
@ -135,23 +143,193 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
|
|||
|
||||
try {
|
||||
em1.getTransaction().begin();
|
||||
Query query = em1.createQuery(
|
||||
"select e.department from Employee e where e.id < 10").setFirstResult(1);
|
||||
// Lock all selected Departments, skip the first one, i.e should lock Department(20)
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
List<Department> q = query.getResultList();
|
||||
assertEquals("Expected 1 element with department id=20", q.size(), 1);
|
||||
assertTrue("Test department name = 'D20'", q.get(0).getName().equals("D10")
|
||||
|| q.get(0).getName().equals("D20"));
|
||||
|
||||
em2.getTransaction().begin();
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
// find Employee(2) with a lock, no block since only department was locked
|
||||
Employee emp = em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
|
||||
assertNotNull("Query locks department only, therefore should find Employee.", emp);
|
||||
assertEquals("Test Employee first name = 'first.1'", emp.getFirstName(), "first.1");
|
||||
} catch (QueryTimeoutException e) {
|
||||
// TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
|
||||
// Remove this when the problem is fixed
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (PessimisticLockException e) {
|
||||
// TODO: This is the expected exception but will be fixed under OPENJPA-991
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
|
||||
} finally {
|
||||
if( em1.getTransaction().isActive())
|
||||
em1.getTransaction().rollback();
|
||||
if( em2.getTransaction().isActive())
|
||||
em2.getTransaction().rollback();
|
||||
}
|
||||
em1.close();
|
||||
em2.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Test find with pessimistic lock after a query with pessimistic lock.
|
||||
*/
|
||||
public void testFindAfterQueryOrderByWithPessimisticLocks() {
|
||||
EntityManager em1 = emf.createEntityManager();
|
||||
EntityManager em2 = emf.createEntityManager();
|
||||
try {
|
||||
em1.getTransaction().begin();
|
||||
Query query = em1.createQuery(
|
||||
"select e from Employee e where e.id < 10 order by e.id").setFirstResult(1);
|
||||
// Lock all selected Employees, skip the first one, i.e should lock Employee(2)
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
List<Employee> q = query.getResultList();
|
||||
assertEquals("Expected 1 element with emplyee id=2", q.size(), 1);
|
||||
assertEquals("Test Employee first name = 'first.2'", q.get(0).getFirstName(), "first.2");
|
||||
|
||||
em2.getTransaction().begin();
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
// find Employee(2) with a lock, should block and expected a PessimisticLockException
|
||||
em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
|
||||
fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
|
||||
} catch (QueryTimeoutException e) {
|
||||
// TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
|
||||
// Remove this when the problem is fixed
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
} catch (PessimisticLockException e) {
|
||||
// TODO: This is the expected exception but will be fixed under OPENJPA-991
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
} catch (Exception ex) {
|
||||
fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
|
||||
} finally {
|
||||
if( em1.getTransaction().isActive())
|
||||
em1.getTransaction().rollback();
|
||||
if( em2.getTransaction().isActive())
|
||||
em2.getTransaction().rollback();
|
||||
}
|
||||
|
||||
try {
|
||||
em1.getTransaction().begin();
|
||||
Query query = em1.createQuery(
|
||||
"select e.department from Employee e where e.id < 10 order by e.department.id").setFirstResult(1);
|
||||
// Lock all selected Departments, skip the first one, i.e should lock Department(20)
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
List<Department> q = query.getResultList();
|
||||
assertEquals("Expected 1 element with department id=20", q.size(), 1);
|
||||
assertEquals("Test department name = 'D20'", q.get(0).getName(), "D20");
|
||||
|
||||
em2.getTransaction().begin();
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
// find Employee(2) with a lock, no block since only department was locked
|
||||
Employee emp = em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
|
||||
assertNotNull("Query locks department only, therefore should find Employee.", emp);
|
||||
assertEquals("Test Employee first name = 'first.1'", emp.getFirstName(), "first.1");
|
||||
} catch (QueryTimeoutException e) {
|
||||
// TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
|
||||
// Remove this when the problem is fixed
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (PessimisticLockException e) {
|
||||
// TODO: This is the expected exception but will be fixed under OPENJPA-991
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
|
||||
} finally {
|
||||
if( em1.getTransaction().isActive())
|
||||
em1.getTransaction().rollback();
|
||||
if( em2.getTransaction().isActive())
|
||||
em2.getTransaction().rollback();
|
||||
}
|
||||
em1.close();
|
||||
em2.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Test query with pessimistic lock after a find with pessimistic lock.
|
||||
*/
|
||||
public void testQueryAfterFindWithPessimisticLocks() {
|
||||
EntityManager em1 = emf.createEntityManager();
|
||||
EntityManager em2 = emf.createEntityManager();
|
||||
try {
|
||||
em2.getTransaction().begin();
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
// Lock Emplyee(1), no department should be locked
|
||||
em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
|
||||
|
||||
em1.getTransaction().begin();
|
||||
Query query = em1.createQuery(
|
||||
"select e.department from Employee e where e.id < 10").setFirstResult(1);
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
// Lock all selected Department but skip the first, i.e. lock Department(20), should query successfully.
|
||||
List<Department> q = query.getResultList();
|
||||
assertEquals("Expected 1 element with department id=20", q.size(), 1);
|
||||
assertTrue("Test department name = 'D20'", q.get(0).getName().equals("D10")
|
||||
|| q.get(0).getName().equals("D20"));
|
||||
} catch (QueryTimeoutException e) {
|
||||
// TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
|
||||
// Remove this when the problem is fixed
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (PessimisticLockException e) {
|
||||
// TODO: This is the expected exception but will be fixed under OPENJPA-991
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
|
||||
} finally {
|
||||
if( em1.getTransaction().isActive())
|
||||
em1.getTransaction().rollback();
|
||||
if (em2.getTransaction().isActive())
|
||||
em2.getTransaction().rollback();
|
||||
}
|
||||
|
||||
try {
|
||||
em2.getTransaction().begin();
|
||||
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
Employee emp = em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
|
||||
assertNotNull("Query locks department but find locks Employee.", emp);
|
||||
fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
|
||||
// Lock Emplyee(2), no department should be locked
|
||||
em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
|
||||
|
||||
em1.getTransaction().begin();
|
||||
Query query = em1.createQuery(
|
||||
"select e from Employee e where e.id < 10").setFirstResult(1);
|
||||
// Lock all selected Employees, skip the first one, i.e should lock Employee(2)
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
List<Employee> q = query.getResultList();
|
||||
fail("Unexcpected find succeeded. Should throw a QueryLockException.");
|
||||
} catch (QueryTimeoutException e) {
|
||||
// TODO: This is the current unexpected exception due to OPENJPA-991. Remove this when the problem is fixed
|
||||
// This is the expected exception.
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
} catch (PessimisticLockException e) {
|
||||
// TODO: This is the expected exception but will be fixed under OPENJPA-991
|
||||
|
@ -169,65 +347,71 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
|
|||
}
|
||||
|
||||
/*
|
||||
* Test a query with pessimistic lock after a find with pessimistic lock and expect PessimisticLockException.
|
||||
* Test query with pessimistic lock after a find with pessimistic lock.
|
||||
*/
|
||||
public void testQueryAfterFindWithPessimisticLocks() {
|
||||
public void testQueryOrderByAfterFindWithPessimisticLocks() {
|
||||
EntityManager em1 = emf.createEntityManager();
|
||||
EntityManager em2 = emf.createEntityManager();
|
||||
try {
|
||||
em2.getTransaction().begin();
|
||||
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
// Lock Emplyee(1), no department should be locked
|
||||
em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
|
||||
|
||||
em1.getTransaction().begin();
|
||||
|
||||
Query query = em1.createQuery(
|
||||
"select e.department from Employee e where e.id < 10 order by e.department.id").setFirstResult(1);
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
List<Department> q = query.getResultList();
|
||||
|
||||
fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
|
||||
} catch (PessimisticLockException e) {
|
||||
// This is the expected exception.
|
||||
} catch (Exception ex) {
|
||||
fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
|
||||
} finally {
|
||||
if( em1.getTransaction().isActive())
|
||||
em1.getTransaction().rollback();
|
||||
if (em2.getTransaction().isActive())
|
||||
em2.getTransaction().rollback();
|
||||
}
|
||||
em1.close();
|
||||
em2.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Test a query with pessimistic lock with query timeout set after a find
|
||||
* with pessimistic lock and expect QueryTimeoutException.
|
||||
*/
|
||||
public void testQueryWithQueryTimeoutAfterFindWithPessimisticLocks() {
|
||||
EntityManager em1 = emf.createEntityManager();
|
||||
EntityManager em2 = emf.createEntityManager();
|
||||
try {
|
||||
em2.getTransaction().begin();
|
||||
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
em2.find(Employee.class, 1, LockModeType.PESSIMISTIC_READ, map);
|
||||
|
||||
em1.getTransaction().begin();
|
||||
|
||||
Query query = em1.createQuery(
|
||||
"select e.department from Employee e where e.id < 10 order by e.department.id").setFirstResult(1);
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
// Lock all selected Department but skip the first, i.e. lock Department(20), should query successfully.
|
||||
List<Department> q = query.getResultList();
|
||||
assertEquals("Expected 1 element with department id=20", q.size(), 1);
|
||||
assertEquals("Test department name = 'D20'", q.get(0).getName(), "D20");
|
||||
} catch (QueryTimeoutException e) {
|
||||
// TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
|
||||
// Remove this when the problem is fixed
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (PessimisticLockException e) {
|
||||
// TODO: This is the expected exception but will be fixed under OPENJPA-991
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
if( !(dict instanceof org.apache.openjpa.jdbc.sql.DB2Dictionary)) {
|
||||
fail("Caught unexpected " + e.getClass().getName() + ":" + e.getMessage());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
|
||||
} finally {
|
||||
if( em1.getTransaction().isActive())
|
||||
em1.getTransaction().rollback();
|
||||
if (em2.getTransaction().isActive())
|
||||
em2.getTransaction().rollback();
|
||||
}
|
||||
|
||||
fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
|
||||
try {
|
||||
em2.getTransaction().begin();
|
||||
|
||||
Map<String,Object> map = new HashMap<String,Object>();
|
||||
map.put("javax.persistence.lock.timeout", 2000);
|
||||
// Lock Emplyee(2), no department should be locked
|
||||
em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
|
||||
|
||||
em1.getTransaction().begin();
|
||||
Query query = em1.createQuery(
|
||||
"select e from Employee e where e.id < 10 order by e.department.id").setFirstResult(1);
|
||||
// Lock all selected Employees, skip the first one, i.e should lock Employee(2)
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
query.setHint("javax.persistence.query.timeout", 2000);
|
||||
List<Employee> q = query.getResultList();
|
||||
fail("Unexcpected find succeeded. Should throw a QueryLockException.");
|
||||
} catch (QueryTimeoutException e) {
|
||||
// This is the expected exception.
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
} catch (PessimisticLockException e) {
|
||||
// TODO: This is the expected exception but will be fixed under OPENJPA-991
|
||||
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
|
||||
} catch (Exception ex) {
|
||||
fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
|
||||
} finally {
|
||||
|
|
Loading…
Reference in New Issue