openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ppod...@apache.org
Subject svn commit: r659669 - in /openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence: cache/common/apps/ datacache/
Date Fri, 23 May 2008 21:35:33 GMT
Author: ppoddar
Date: Fri May 23 14:35:32 2008
New Revision: 659669

URL: http://svn.apache.org/viewvc?rev=659669&view=rev
Log:
OPENJPA-610 Test cases to identify use cases where behavior differs because of DataCache

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwned.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwner.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwned.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwner.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwned.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwned.java?rev=659669&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwned.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwned.java
Fri May 23 14:35:32 2008
@@ -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;
+	}
+
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwner.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwner.java?rev=659669&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwner.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/BidirectionalOne2OneOwner.java
Fri May 23 14:35:32 2008
@@ -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;
+	}
+
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwned.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwned.java?rev=659669&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwned.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwned.java
Fri May 23 14:35:32 2008
@@ -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;
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwner.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwner.java?rev=659669&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwner.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/cache/common/apps/UnidirectionalOne2OneOwner.java
Fri May 23 14:35:32 2008
@@ -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;
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java?rev=659669&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java
Fri May 23 14:35:32 2008
@@ -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);
+	}
+
+}



Mime
View raw message