OPENJPA-610 Test cases to identify use cases where behavior differs because of DataCache

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@659669 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Pinaki Poddar 2008-05-23 21:35:32 +00:00
parent 93e15d846c
commit 534778acf8
5 changed files with 783 additions and 0 deletions

View File

@ -0,0 +1,89 @@
package org.apache.openjpa.persistence.cache.common.apps;
import javax.persistence.*;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* A persistent entity that is owned by unidirectional single-valued
* relationship.
* A unidirectional relationship has only one owning side and the other side
* called as owned side is this receiver.
* Given the following relationship between Entity A and Entity B:
* Entity A refers a single instance of Entity B
* Entity B does not refer Entity A (owner)
* Entity A is called owner and Entity B is called owned with respect
* to the above relationship.
*
* Used to test identical application behavior with or without DataCache.
*
* @see BidirectionalOne2OneOwned
* @see TestDataCacheBehavesIdentical
* @see Section 2.1.8.3 of JPA Specification Version 1.0
*
* @author Pinaki Poddar
*
*/
@Entity
public class BidirectionalOne2OneOwned {
@Id
private long id;
private String name;
@OneToOne(mappedBy="owned")
private BidirectionalOne2OneOwner owner;
@Version
private int version;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BidirectionalOne2OneOwner getOwner() {
return owner;
}
public void setOwner(BidirectionalOne2OneOwner owner) {
this.owner = owner;
}
public int getVersion() {
return version;
}
public String toString() {
return this.getClass().getSimpleName() + ":" + id + ":" + name;
}
}

View File

@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.cache.common.apps;
import javax.persistence.*;
/**
* A persistent entity that owns bidirectional single-valued relationship.
* A bidirectional relationship has only an owning side, which is this receiver.
* Given the following relationship between Entity A and Entity B:
* Entity A refers to a single instance of Entity B
* Entity B refers to a single instance of Entity A
* If Entity B qualifies its relation to the Entity A with mappedBy
* annotation qualifier then Entity B is called owned and Entity A is called
* owner with respect to the above relationship.
*
* Used to test identical application behavior with or without DataCache.
*
* @see BidirectionalOne2OneOwned
*
* @author Pinaki Poddar
*
*/
@Entity
public class BidirectionalOne2OneOwner {
@Id
private long id;
private String name;
@OneToOne
private BidirectionalOne2OneOwned owned;
@Version
private int version;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BidirectionalOne2OneOwned getOwned() {
return owned;
}
public void setOwned(BidirectionalOne2OneOwned owned) {
this.owned = owned;
}
public int getVersion() {
return version;
}
public String toString() {
return this.getClass().getSimpleName() + ":" + id + ":" + name;
}
}

View File

@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.cache.common.apps;
import javax.persistence.*;
/**
* A persistent entity that is owned by unidirectional single-valued
* relationship.
* A unidirectional relationship has only one owning side and the other side
* called as owned side is this receiver.
* Given the following relationship between Entity A and Entity B:
* Entity A refers a single instance of Entity B
* Entity B does not refer Entity A (owner)
* Entity A is called owner and Entity B is called owned with respect
* to the above relationship.
*
* Used to test identical application behavior with or without DataCache.
*
* @see UnidirectionalOne2OneOwned
* @see TestDataCacheBehavesIdentical
* @see Section 2.1.8.3 of JPA Specification Version 1.0
*
* @author Pinaki Poddar
*
*/
@Entity
public class UnidirectionalOne2OneOwned {
@Id
private long id;
private String name;
@Version
private int version;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getVersion() {
return version;
}
public String toString() {
return this.getClass().getSimpleName() + ":" + id + ":" + name;
}
}

View File

@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.cache.common.apps;
import javax.persistence.*;
/**
* A persistent entity that owns unidirectional single-valued relationship.
* A unidirectional relationship has only one owning side, which is this
* receiver.
* Given the following relationship between Entity A and Entity B:
* Entity A refers a single instance of Entity B
* Entity B does not refer Entity A (owner)
* Entity A is called owner and Entity B is called owned with respect
* to the above relationship.
*
* Used to test identical application behavior with or without DataCache.
*
* @see UnidirectionalOne2OneOwned
* @see TestDataCacheBehavesIdentical
* @see Section 2.1.8.3 of JPA Specification Version 1.0
*
* @author Pinaki Poddar
*
*/
@Entity
public class UnidirectionalOne2OneOwner {
@Id
private long id;
private String name;
@OneToOne
private UnidirectionalOne2OneOwned owned;
@Version
private int version;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public UnidirectionalOne2OneOwned getOwned() {
return owned;
}
public void setOwned(UnidirectionalOne2OneOwned owned) {
this.owned = owned;
}
public int getVersion() {
return version;
}
public String toString() {
return this.getClass().getSimpleName() + ":" + id + ":" + name;
}
}

View File

@ -0,0 +1,444 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.datacache;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.LockModeType;
import org.apache.openjpa.persistence.EntityManagerImpl;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
import org.apache.openjpa.persistence.StoreCache;
import org.apache.openjpa.persistence.StoreCacheImpl;
import org.apache.openjpa.persistence.cache.common.apps.BidirectionalOne2OneOwned;
import org.apache.openjpa.persistence.cache.common.apps.BidirectionalOne2OneOwner;
import org.apache.openjpa.persistence.common.utils.AbstractTestCase;
import org.apache.openjpa.persistence.exception.PObject;
/**
* Tests various application behavior with or without DataCache.
* Ideally, an application should behave identically irrespective of the
* DataCache. However, purpose of this test is to identify specific scenarios
* where this ideal is violated. The test case also demonstrates, wherever
* possible, what extra step an application may take to ensure that its
* behavior with or without DataCache remains identical.
*
* So far following use cases are found to demonstrate behavioral differences:
* 1. Inconsistent bidirectional relation
* 2. Refresh
*
* @author Pinaki Poddar
*
*/
public class TestDataCacheBehavesIdentical extends AbstractTestCase {
private static OpenJPAEntityManagerFactorySPI emfWithDataCache;
private static OpenJPAEntityManagerFactorySPI emfWithoutDataCache;
private static final boolean WITH_DATACACHE = true;
private static final boolean CONSISTENT = true;
private static final boolean DIRTY = true;
private static final LockModeType NOLOCK = null;
private static final String MARKER_DATACACHE = "in DataCache";
private static final String MARKER_DATABASE = "in Database";
private static final String MARKER_CACHE = "in Object Cache";
private static final String MARKER_DIRTY_CACHE = "in Object Cache (dirty)";
private static long ID_COUNTER = System.currentTimeMillis();
/**
* Sets up two EntityManagerFactory: one with DataCache another without.
*/
public void setUp() throws Exception {
super.setUp();
if (emfWithDataCache == null) {
emfWithDataCache = createEMF(
"openjpa.jdbc.SynchronizeMappings", "buildSchema",
"openjpa.RuntimeUnenhancedClasses", "unsupported",
"openjpa.DataCache", "true",
"openjpa.RemoteCommitProvider", "sjvm",
"openjpa.jdbc.UpdateManager", "constraint",
PObject.class,
BidirectionalOne2OneOwner.class,
BidirectionalOne2OneOwned.class, CLEAR_TABLES);
emfWithoutDataCache = createEMF(
"openjpa.RuntimeUnenhancedClasses", "unsupported",
"openjpa.DataCache", "false",
"openjpa.jdbc.UpdateManager", "constraint",
PObject.class,
BidirectionalOne2OneOwned.class,
BidirectionalOne2OneOwner.class, CLEAR_TABLES);
assertNotNull(emfWithDataCache);
assertNotNull(emfWithoutDataCache);
// StoreCache is, by design, always non-null
assertNotNull(emfWithDataCache.getStoreCache());
assertNotNull(emfWithoutDataCache.getStoreCache());
// however, following distinguishes whether DataCache is active
assertTrue(isDataCacheActive(emfWithDataCache));
assertFalse(isDataCacheActive(emfWithoutDataCache));
}
}
/**
* Affirms via internal structures if the given factory is configured with
* active DataCache. Because, even when DataCache is configured to be
* false, a no-op StoreCache is instantiated by design.
*/
boolean isDataCacheActive(OpenJPAEntityManagerFactorySPI emf) {
return ((StoreCacheImpl) emf.getStoreCache()).getDelegate() != null
&& emf.getConfiguration()
.getDataCacheManagerInstance()
.getSystemDataCache() != null;
}
/**
* Create one-to-one bidirectional relation (may or may not be consistent)
* between two pairs of instances. Creates four instances Owner1, Owned1,
* Owner2, Owned2. The first instance has the given id. The id of the other
* instances monotonically increase by 1. The relationship is set either
* consistently or inconsistently. Consistent relation is when Owner1 points
* to Owned1 and Owned1 points back to Owner1. Inconsistent relation is when
* Owner1 points to Owned1 but Owned1 points to Owner2 instead of Owner1.
*
*
* @param em
* the entity manager to persist the instances
* @param id
* the identifier of the first owner instance. The identifier for
* the other instances are sequential in order of creation.
* @param consistent
* if true sets the relationship as consistent.
*/
public void createBidirectionalRelation(EntityManager em, long id,
boolean consistent) {
BidirectionalOne2OneOwner owner1 = new BidirectionalOne2OneOwner();
BidirectionalOne2OneOwned owned1 = new BidirectionalOne2OneOwned();
BidirectionalOne2OneOwner owner2 = new BidirectionalOne2OneOwner();
BidirectionalOne2OneOwned owned2 = new BidirectionalOne2OneOwned();
owner1.setId(id++);
owned1.setId(id++);
owner2.setId(id++);
owned2.setId(id++);
owner1.setName("Owner1");
owned1.setName("Owned1");
owned2.setName("Owned2");
owner2.setName("Owner2");
owner1.setOwned(owned1);
owner2.setOwned(owned2);
if (consistent) {
owned1.setOwner(owner1);
owned2.setOwner(owner2);
} else {
owned1.setOwner(owner2);
owned2.setOwner(owner1);
}
em.getTransaction().begin();
em.persist(owner1);
em.persist(owned1);
em.persist(owner2);
em.persist(owned2);
em.getTransaction().commit();
em.clear();
}
/**
* Verifies that bidirectionally related objects can be persisted
* and later retrieved in a different transaction.
*
* Creates interrelated set of four instances.
* Establish their relation either consistently or inconsistently based
* on the given flag.
* Persist them and then clear the context.
* Fetch the instances in memory again by their identifiers.
* Compare the interrelations between the fetched instances with the
* relations of the original instances (which can be consistent or
* inconsistent).
*
* The mapping specification is such that the bidirectional relation is
* stored in database by a single foreign key. Hence database relation
* is always consistent. Hence the instances retrieved from database are
* always consistently related irrespective of whether they were created
* with consistent or inconsistent relation.
* However, when the instances are retrieved from the data cache, data cache
* will preserve the in-memory relations even when they are inconsistent.
*
* @param useDataCache
* use DataCache
* @param consistent
* assume that the relationship were created as consistent.
*/
public void verifyBidirectionalRelation(boolean useDataCache,
boolean createConsistent, boolean expectConsistent) {
EntityManager em = (useDataCache)
? emfWithDataCache.createEntityManager()
: emfWithoutDataCache.createEntityManager();
long id = ID_COUNTER++;
ID_COUNTER += 4;
createBidirectionalRelation(em, id, createConsistent);
BidirectionalOne2OneOwner owner1 = em.find(BidirectionalOne2OneOwner.class, id);
BidirectionalOne2OneOwned owned1 = em.find(BidirectionalOne2OneOwned.class, id + 1);
BidirectionalOne2OneOwner owner2 = em.find(BidirectionalOne2OneOwner.class, id + 2);
BidirectionalOne2OneOwned owned2 = em.find(BidirectionalOne2OneOwned.class, id + 3);
assertNotNull(owner1);
assertNotNull(owner2);
assertNotNull(owned1);
assertNotNull(owned2);
assertEquals(owner1, expectConsistent
? owner1.getOwned().getOwner()
: owner2.getOwned().getOwner());
assertEquals(owner2, expectConsistent
? owner2.getOwned().getOwner()
: owner1.getOwned().getOwner());
assertEquals(owned1, owner1.getOwned());
assertEquals(expectConsistent ? owner1 : owner2, owned1.getOwner());
assertEquals(owned2, owner2.getOwned());
assertEquals(expectConsistent ? owner2 : owner1, owned2.getOwner());
}
public void testConsitentBidirectionalRelationIsPreservedWithDataCache() {
verifyBidirectionalRelation(WITH_DATACACHE, CONSISTENT, CONSISTENT);
}
public void testConsitentBidirectionalRelationIsPreservedWithoutDataCache() {
verifyBidirectionalRelation(!WITH_DATACACHE, CONSISTENT, CONSISTENT);
}
public void testInconsitentBidirectionalRelationIsPreservedWithDataCache() {
verifyBidirectionalRelation(WITH_DATACACHE, !CONSISTENT, !CONSISTENT);
}
public void testInconsitentBidirectionalRelationIsNotPreservedWithoutDataCache() {
verifyBidirectionalRelation(!WITH_DATACACHE, !CONSISTENT, CONSISTENT);
}
/**
* Verify that refresh() may fetch state from either the data cache or the
* database based on different conditions.
* The conditions that impact are
* a) whether current lock is stronger than NONE
* b) whether the instance being refreshed is dirty
*
* An instance is created with data cache marker and persisted.
* A native SQL is used to update the database record with database marker.
* The in-memory instance is not aware of this out-of-band update.
* Then the in-memory instance is refreshed. The marker of the refreshed
* instance tells whether the instance is refreshed from the data cache
* of the database.
*
* @param useDataCache flags if data cache is active. if not, then surely
* refresh always fetch state from the database.
*
* @param datacache the marker for the copy of the data cached instance
* @param database the marker for the database record
* @param lock lock to be used
*/
public void verifyRefresh(boolean useDataCache, LockModeType lock, boolean makeDirtyBeforeRefresh) {
OpenJPAEntityManagerFactorySPI emf = (useDataCache)
? emfWithDataCache : emfWithoutDataCache;
OpenJPAEntityManagerSPI em = emf.createEntityManager();
em.getTransaction().begin();
PObject pc = new PObject();
pc.setName(useDataCache ? MARKER_DATACACHE : MARKER_CACHE);
em.persist(pc);
em.getTransaction().commit();
Object oid = pc.getId();
StoreCache dataCache = emf.getStoreCache();
assertEquals(useDataCache, dataCache.contains(PObject.class, oid));
// Modify the record in the database in a separate transaction using
// native SQL so that the in-memory instance is not altered
em.getTransaction().begin();
String sql = "UPDATE POBJECT SET NAME='"+ MARKER_DATABASE +"' WHERE id="+oid;
em.createNativeQuery(sql).executeUpdate();
em.getTransaction().commit();
assertEquals(useDataCache ? MARKER_DATACACHE : MARKER_CACHE, pc.getName());
em.getTransaction().begin();
if (makeDirtyBeforeRefresh) {
pc.setName(MARKER_DIRTY_CACHE);
}
assertEquals(makeDirtyBeforeRefresh, em.isDirty(pc));
if (lock != null) {
((EntityManagerImpl)em).getFetchPlan().setReadLockMode(lock);
}
em.refresh(pc);
String expected = getExpectedMarker(useDataCache, lock, makeDirtyBeforeRefresh);
assertEquals(expected, pc.getName());
em.getTransaction().commit();
}
/**
* The expected marker i.e. where the state is refreshed from depends on
* a) whether DataCache is active
* b) whether current Lock is stronger than NOLOCK
* c) whether the object to be refreshed is dirty
*
* The following truth table enumerates the possibilities
*
* Use Cache? Lock? Dirty? Target
* Y Y Y Database
* Y N Y Data Cache
* Y Y N Data Cache
* Y N N Data Cache
*
* N Y Y Database
* N N Y Database
* N Y N Object Cache
* N N N Object Cache
*
* @param datacache the marker for
* @param database
* @param useDataCache
* @param lock
* @param makeDirtyBeforeRefresh
* @return
*/
String getExpectedMarker(boolean useDataCache, LockModeType lock, boolean makeDirtyBeforeRefresh) {
if (useDataCache) {
return (lock != null && makeDirtyBeforeRefresh)
? MARKER_DATABASE : MARKER_DATACACHE;
} else {
return (makeDirtyBeforeRefresh) ? MARKER_DATABASE : MARKER_CACHE;
}
}
public void testDirtyRefreshWithNoLockHitsDataCache() {
verifyRefresh(WITH_DATACACHE, NOLOCK, DIRTY);
}
public void testCleanRefreshWithNoLockHitsDataCache() {
verifyRefresh(WITH_DATACACHE, NOLOCK, !DIRTY);
}
public void testDirtyRefreshWithReadLockHitsDatabase() {
verifyRefresh(WITH_DATACACHE, LockModeType.READ, DIRTY);
}
public void testCleanRefreshWithReadLockHitsDataCache() {
verifyRefresh(WITH_DATACACHE, LockModeType.READ, !DIRTY);
}
public void testDirtyRefreshWithWriteLockHitsDatabase() {
verifyRefresh(WITH_DATACACHE, LockModeType.WRITE, DIRTY);
}
public void testCleanRefreshWithWriteLockHitsDatabase() {
verifyRefresh(WITH_DATACACHE, LockModeType.WRITE, !DIRTY);
}
public void testDirtyRefreshWithoutDataCacheAlwaysHitsDatabase() {
verifyRefresh(!WITH_DATACACHE, NOLOCK, DIRTY);
verifyRefresh(!WITH_DATACACHE, LockModeType.READ, DIRTY);
verifyRefresh(!WITH_DATACACHE, LockModeType.WRITE, DIRTY);
}
public void testCleanRefreshWithoutDataCacheNeverHitsDatabase() {
verifyRefresh(!WITH_DATACACHE, NOLOCK, !DIRTY);
verifyRefresh(!WITH_DATACACHE, LockModeType.READ, !DIRTY);
verifyRefresh(!WITH_DATACACHE, LockModeType.WRITE, !DIRTY);
}
public void verifyDeleteDetectionOnRefresh(boolean useDataCache, LockModeType lock) {
OpenJPAEntityManagerFactorySPI emf = (useDataCache)
? emfWithDataCache : emfWithoutDataCache;
OpenJPAEntityManagerSPI em = emf.createEntityManager();
em.getTransaction().begin();
PObject pc = new PObject();
pc.setName(useDataCache ? MARKER_DATACACHE : MARKER_CACHE);
em.persist(pc);
em.getTransaction().commit();
Object oid = pc.getId();
StoreCache dataCache = emf.getStoreCache();
assertEquals(useDataCache, dataCache.contains(PObject.class, oid));
// delete the record in the database in a separate transaction using
// native SQL so that the in-memory instance is not altered
em.getTransaction().begin();
String sql = "DELETE FROM POBJECT WHERE id="+oid;
em.createNativeQuery(sql).executeUpdate();
em.getTransaction().commit();
// the object cache does not know that the record was deleted
assertTrue(em.contains(pc));
// nor does the data cache
assertEquals(useDataCache, dataCache.contains(PObject.class, oid));
assertEquals(useDataCache ? MARKER_DATACACHE : MARKER_CACHE, pc.getName());
em.getTransaction().begin();
if (lock != null)
em.getFetchPlan().setReadLockMode(lock);
try {
em.refresh(pc);
if (lock == null) {
assertEquals(useDataCache ? MARKER_DATACACHE : MARKER_CACHE, pc.getName());
} else {
fail("expected EntityNotFoundException for PObject:" + oid);
}
} catch (EntityNotFoundException ex) {
if (lock != null) {
// we are good
}
} finally {
em.getTransaction().rollback();
}
}
public void testDeleteIsDetectedOnRefreshWithLockWithActiveDataCache() {
verifyDeleteDetectionOnRefresh(WITH_DATACACHE, LockModeType.READ);
verifyDeleteDetectionOnRefresh(WITH_DATACACHE, LockModeType.WRITE);
}
public void testDeleteIsNotDetectedOnRefreshWithNoLockWithActiveDataCache() {
verifyDeleteDetectionOnRefresh(WITH_DATACACHE, NOLOCK);
}
public void testDeleteIsDetectedOnRefreshAlwaysWithoutDataCache() {
verifyDeleteDetectionOnRefresh(!WITH_DATACACHE, NOLOCK);
verifyDeleteDetectionOnRefresh(!WITH_DATACACHE, LockModeType.READ);
verifyDeleteDetectionOnRefresh(!WITH_DATACACHE, LockModeType.WRITE);
}
}