From 3edd740d81e91e5aa60b14f6baaff0d0820d556b Mon Sep 17 00:00:00 2001 From: Jody Grassel Date: Wed, 21 Aug 2013 16:15:00 +0000 Subject: [PATCH] OPENJPA-2414: FinderCache does not consider active Fetch Groups/FetchPlan added Fields git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.1.x@1516197 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/jdbc/kernel/FinderCacheImpl.java | 12 + .../kernel/DelegatingFetchConfiguration.java | 8 + .../openjpa/kernel/FetchConfiguration.java | 6 + .../kernel/FetchConfigurationImpl.java | 74 +- .../fetchgroups/TestFetchGroups.java | 919 ++++++++++++++++++ 5 files changed, 1007 insertions(+), 12 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/TestFetchGroups.java diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java index d423b3d0b..21c393c5a 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java @@ -107,6 +107,12 @@ public class FinderCacheImpl if (fetch.getReadLockLevel() != 0) { return null; } + + // FinderCache only operates with Default Fetch Plans + if (!fetch.isDefaultPUFetchGroupConfigurationOnly()) { + return null; + } + boolean ignore = isHinted(fetch, QueryHints.HINT_IGNORE_FINDER); boolean invalidate = isHinted(fetch, QueryHints.HINT_INVALIDATE_FINDER); if (invalidate) { @@ -144,6 +150,12 @@ public class FinderCacheImpl if (fetch.getReadLockLevel() != 0) { return null; } + + // FinderCache only operates with Default Fetch Plans + if (!fetch.isDefaultPUFetchGroupConfigurationOnly()) { + return null; + } + boolean recache = isHinted(fetch, QueryHints.HINT_RECACHE_FINDER); if (isExcluded(mapping)) { return recache ? put(mapping, select) : null; diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java index 13dfc4fd8..2c426a2d6 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java @@ -587,4 +587,12 @@ public class DelegatingFetchConfiguration throw translate(re); } } + + public boolean isDefaultPUFetchGroupConfigurationOnly() { + try { + return _fetch.isDefaultPUFetchGroupConfigurationOnly(); + } catch (RuntimeException re) { + throw translate(re); + } + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java index a1f01cbb4..7bc852d9e 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java @@ -468,4 +468,10 @@ public interface FetchConfiguration * @since 0.4.1 */ public FetchConfiguration traverse(FieldMetaData fm); + + /** + * Affirm if the Fetch Plan currently matches the Persistence Unit's configured default. + * + */ + public boolean isDefaultPUFetchGroupConfigurationOnly(); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java index 1791c95f0..e9cdd8794 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java @@ -145,6 +145,7 @@ public class FetchConfigurationImpl public Map hints = null; public boolean fetchGroupContainsDefault = false; public boolean fetchGroupContainsAll = false; + public boolean fetchGroupIsPUDefault = false; public boolean extendedPathLookup = false; public DataCacheRetrieveMode cacheRetrieveMode = DataCacheRetrieveMode.USE; public DataCacheStoreMode cacheStoreMode = DataCacheStoreMode.USE; @@ -328,17 +329,21 @@ public class FetchConfigurationImpl || _state.fetchGroups.contains(FetchGroup.NAME_ALL)); } - public boolean hasFetchGroupDefault() { - // Fetch group All includes fetch group Default by definition - return _state.fetchGroupContainsDefault || - _state.fetchGroupContainsAll; - } - - public boolean hasFetchGroupAll() { - return _state.fetchGroupContainsAll; - } - + public boolean hasFetchGroupDefault() { + // Fetch group All includes fetch group Default by definition + return _state.fetchGroupContainsDefault || + _state.fetchGroupContainsAll; + } + + public boolean hasFetchGroupAll() { + return _state.fetchGroupContainsAll; + } + public FetchConfiguration addFetchGroup(String name) { + return addFetchGroup(name, true); + } + + private FetchConfiguration addFetchGroup(String name, boolean recomputeIsDefault) { if (StringUtils.isEmpty(name)) throw new UserException(_loc.get("null-fg")); @@ -352,6 +357,9 @@ public class FetchConfigurationImpl else if (FetchGroup.NAME_DEFAULT.equals(name)) _state.fetchGroupContainsDefault = true; } finally { + if (recomputeIsDefault) { + verifyDefaultPUFetchGroups(); + } unlock(); } return this; @@ -361,11 +369,17 @@ public class FetchConfigurationImpl if (groups == null || groups.isEmpty()) return this; for (String group : groups) - addFetchGroup(group); + addFetchGroup(group, false); + + verifyDefaultPUFetchGroups(); return this; } public FetchConfiguration removeFetchGroup(String group) { + return removeFetchGroup(group, true); + } + + private FetchConfiguration removeFetchGroup(String group, boolean recomputeIsDefault) { lock(); try { if (_state.fetchGroups != null) { @@ -376,6 +390,9 @@ public class FetchConfigurationImpl _state.fetchGroupContainsDefault = false; } } finally { + if (recomputeIsDefault) { + verifyDefaultPUFetchGroups(); + } unlock(); } return this; @@ -386,8 +403,9 @@ public class FetchConfigurationImpl try { if (_state.fetchGroups != null && groups != null) for (String group : groups) - removeFetchGroup(group); + removeFetchGroup(group, false); } finally { + verifyDefaultPUFetchGroups(); unlock(); } return this; @@ -402,6 +420,7 @@ public class FetchConfigurationImpl _state.fetchGroupContainsDefault = true; } } finally { + verifyDefaultPUFetchGroups(); unlock(); } return this; @@ -414,6 +433,37 @@ public class FetchConfigurationImpl getFetchGroupsList())); return this; } + + /** + * Determine if the current selection of FetchGroups is equivalent to the Configuration's default FetchGroups + * + */ + private void verifyDefaultPUFetchGroups() { + _state.fetchGroupIsPUDefault = false; + + if (_state.fields != null && !_state.fields.isEmpty()) { + return; + } + + if (_state.fetchGroups != null && _state.ctx != null) { + List defaultPUFetchGroups = Arrays.asList(_state.ctx.getConfiguration().getFetchGroupsList()); + if (_state.fetchGroups.size() != defaultPUFetchGroups.size()) { + return; + } + + for (String fetchGroupName : defaultPUFetchGroups) { + if (!_state.fetchGroups.contains(fetchGroupName)) { + return; + } + } + + _state.fetchGroupIsPUDefault = true; + } + } + + public boolean isDefaultPUFetchGroupConfigurationOnly() { + return _state.fetchGroupIsPUDefault; + } public Set getFields() { if (_state.fields == null) return Collections.emptySet(); diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/TestFetchGroups.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/TestFetchGroups.java new file mode 100644 index 000000000..d33eb2a0f --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fetchgroups/TestFetchGroups.java @@ -0,0 +1,919 @@ +/* + * 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.fetchgroups; + +import java.util.HashSet; + +import org.apache.openjpa.persistence.FetchPlan; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; +import org.apache.openjpa.persistence.test.SingleEMTestCase; + +public class TestFetchGroups extends SingleEMTestCase { + private static final int empPerMgr = 5; + private static final int mgrCount = 3; + private static final int empCount = mgrCount * empPerMgr; + + + private HashSet employeeSet = new HashSet(); + private HashSet managerSet = new HashSet(); + + public void setUp() { + super.setUp(CLEAR_TABLES, + FGManager.class, FGDepartment.class, FGEmployee.class, FGAddress.class); + createEmployeeData(); + } + + /** + * Verify the "default" fetch plan that models JPA's expected eager/lazy fetch load behaviors. + */ + public void testDefaultFetchPlan001() { + OpenJPAEntityManager em = emf.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.close(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + /** + * Verify that adding a FetchGroup to the fetch plan makes a normally JPA determined lazy loaded + * field to behave as an eagerly loaded field. + */ + public void testDefaultFetchPlan002() { + OpenJPAEntityManager em = emf.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.close(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + /** + * Verify the use of the "openjpa.FetchGroups" property when used to add a fetch group + * to the default fetch plan. Note when overriding that "default" must be included in the list. + */ + public void testPctxDefaultFetchPlan001() { + OpenJPAEntityManagerFactory emf2 = createNamedEMF(getPersistenceUnitName(), + FGManager.class, FGDepartment.class, FGEmployee.class, FGAddress.class, + "openjpa.FetchGroups", "default,DescFetchGroup"); + + OpenJPAEntityManager em = emf2.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.close(); + emf2.close(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + /** + * Verify the use of the "openjpa.FetchGroups" property - when a list not containing "default" + * is provided, then the PCtx's default fetch plan should not include it. This renders + * fields normally eagerly loaded as per JPA rules to behave as lazy loaded fields. + * + * Note that fetch groups are case sensitive, "default" != "Default". + */ + public void testPctxDefaultFetchPlan002() { + OpenJPAEntityManagerFactory emf2 = createNamedEMF(getPersistenceUnitName(), + FGManager.class, FGDepartment.class, FGEmployee.class, FGAddress.class, + "openjpa.FetchGroups", "Default,DescFetchGroup"); + + OpenJPAEntityManager em = emf2.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("Default")); // Not the same as "default" + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.close(); + emf2.close(); + + assertEquals(mgr.getId(), findMgr.getId()); // Identity is always loaded + assertNull(findMgr.getFirstName()); + assertNull(findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + /** + * Test clearFetchGroups(), which removes all fetch groups from the fetch plan and reactivates + * the "default" fetch plan. + * + * Note that the method does not place "default" back in the list of active fetch groups, OPENJPA-2413 + * was opened to note this behavior. + */ + public void testClearFetchGroups001() { + OpenJPAEntityManagerFactory emf2 = createNamedEMF(getPersistenceUnitName(), + FGManager.class, FGDepartment.class, FGEmployee.class, FGAddress.class, + "openjpa.FetchGroups", "Default,DescFetchGroup"); + + OpenJPAEntityManager em = emf2.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("Default")); // Not the same as "default" + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + fp.clearFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(0, fp.getFetchGroups().size()); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.close(); + emf2.close(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + /** + * The resetFetchGroups() method restores the fetch plan's active fetch plans to + * the PCtx's configured default. + */ + public void testResetFetchGroups001() { + OpenJPAEntityManagerFactory emf2 = createNamedEMF(getPersistenceUnitName(), + FGManager.class, FGDepartment.class, FGEmployee.class, FGAddress.class, + "openjpa.FetchGroups", "Default,DescFetchGroup"); + + OpenJPAEntityManager em = emf2.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("Default")); // Not the same as "default" + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + // Remember, OPENJPA-2413 sets the list to be empty instead of containing "default" + fp.clearFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(0, fp.getFetchGroups().size()); + + // Reset to the PCtx default Fetch Plan + fp.resetFetchGroups(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("Default")); // Not the same as "default" + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + // Verify that the PCtx default fetch plan was properly restored. "default" should not be enabled + // since it was not listed by openjpa.FetchGroups. + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.close(); + emf2.close(); + + assertEquals(mgr.getId(), findMgr.getId()); // Identity is always loaded +// assertNull(findMgr.getFirstName()); // Commented out, for OPENJPA-2420 +// assertNull(findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + /** + * Baseline test for Finder Cache + */ + public void testFinderCache001() { + OpenJPAEntityManager em = emf.createEntityManager(); + + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + { + // First find, to prime the Finder Cache + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + em.close(); + } + + /** + * Only SQL generated by the PCtx's default fetch plan should be used by the finder cache, + * as it currently lacks the ability to distinguish fetch plan configuration in its key value. + * The PCtx's default fetch plan is the normal plan not modified by the "openjpa.FetchGroups" + * property. + * + * In this variant, a find using the default fetch plan is first executed to prime the finder cache. + * Finds operating under a modified fetch plan should not utilize sql stored in the finder cache. + */ + public void testFinderCache002() { + OpenJPAEntityManager em = emf.createEntityManager(); + + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + { + // First find, to prime the Finder Cache + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Add a fetch group to the fetch plan and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Remove the fetch group previously added, and verify expected behavior + fp.removeFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Add a fetch group to the fetch plan and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Reset the fetch plan, and verify expected behavior + fp.resetFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Add a fetch group to the fetch plan and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Clear all fetch groups, and verify expected behavior + // Note, due to OPENJPA-2413 even though it behaves as if the "default" fetch group is enabled, + // it will not show up in the list of active fetch groups. + fp.clearFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(0, fp.getFetchGroups().size()); + assertFalse(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + em.close(); + } + + /** + * Only SQL generated by the PCtx's default fetch plan should be used by the finder cache, + * as it currently lacks the ability to distinguish fetch plan configuration in its key value. + * The PCtx's default fetch plan is the normal plan not modified by the "openjpa.FetchGroups" + * property. + * + * In this variant, a find using a modified fetch plan is first executed, which should not be added + * to the finder cache. + */ + public void testFinderCache003() { + OpenJPAEntityManager em = emf.createEntityManager(); + + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Remove the "DescFetchGroup" fetch group, and verify expected behavior + fp.removeFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Restore the fetch group to the fetch plan and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Remove the "DescFetchGroup" fetch group, and verify expected behavior + fp.removeFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Restore the fetch group to the fetch plan and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Reset the fetch plan, and verify expected behavior + fp.resetFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Restore the fetch group to the fetch plan and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Clear all fetch groups, and verify expected behavior + // Note, due to OPENJPA-2413 even though it behaves as if the "default" fetch group is enabled, + // it will not show up in the list of active fetch groups. + fp.clearFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(0, fp.getFetchGroups().size()); + assertFalse(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + em.close(); + } + + /** + * Only SQL generated by the PCtx's default fetch plan should be used by the finder cache, + * as it currently lacks the ability to distinguish fetch plan configuration in its key value. + * The PCtx's default fetch plan is modified by the "openjpa.FetchGroups" property. + * + */ + public void testFinderCache004() { + OpenJPAEntityManagerFactory emf2 = createNamedEMF(getPersistenceUnitName(), + FGManager.class, FGDepartment.class, FGEmployee.class, FGAddress.class, + "openjpa.FetchGroups", "default,DescFetchGroup"); + + OpenJPAEntityManager em = emf2.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + { + // First find, to prime the Finder Cache + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Remove a fetch group to the fetch plan and verify expected behavior + fp.removeFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Restore the fetch group previously removed, and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Remove a fetch group to the fetch plan and verify expected behavior + fp.removeFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + // Second find, should rely on the finder cache to reuse generated SQL. + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Reset the fetch plan, and verify expected behavior + fp.resetFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Clear all fetch groups, and verify expected behavior + // Note, due to OPENJPA-2413 even though it behaves as if the "default" fetch group is enabled, + // it will not show up in the list of active fetch groups. + fp.clearFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(0, fp.getFetchGroups().size()); + assertFalse(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + em.close(); + emf2.close(); + } + + /** + * Only SQL generated by the PCtx's default fetch plan should be used by the finder cache, + * as it currently lacks the ability to distinguish fetch plan configuration in its key value. + * The PCtx's default fetch plan is modified by the "openjpa.FetchGroups" property. + * + * In this variant, a find using a modified fetch plan is first executed, which should not be added + * to the finder cache. + */ + public void testFinderCache005() { + OpenJPAEntityManagerFactory emf2 = createNamedEMF(getPersistenceUnitName(), + FGManager.class, FGDepartment.class, FGEmployee.class, FGAddress.class, + "openjpa.FetchGroups", "default,DescFetchGroup"); + + OpenJPAEntityManager em = emf2.createEntityManager(); + FetchPlan fp = em.getFetchPlan(); + assertNotNull(fp); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + + FGManager mgr = managerSet.iterator().next(); + assertNotNull(mgr); + + fp.removeFetchGroup("DescFetchGroup"); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Restore the "DescFetchGroup" fetch group, and verify expected behavior + fp.addFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Remove the "DescFetchGroup" fetch group, and verify expected behavior + fp.removeFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Reset the fetch plan, and verify expected behavior + fp.resetFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(2, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertTrue(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertEquals(mgr.getDescription(), findMgr.getDescription()); // Should not be lazy-loaded + } + + // Remove the "DescFetchGroup" fetch group, and verify expected behavior + fp.removeFetchGroup("DescFetchGroup"); + assertNotNull(fp.getFetchGroups()); + assertEquals(1, fp.getFetchGroups().size()); + assertTrue(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + // Clear all fetch groups, and verify expected behavior + // Note, due to OPENJPA-2413 even though it behaves as if the "default" fetch group is enabled, + // it will not show up in the list of active fetch groups. + fp.clearFetchGroups(); + assertNotNull(fp.getFetchGroups()); + assertEquals(0, fp.getFetchGroups().size()); + assertFalse(fp.getFetchGroups().contains("default")); + assertFalse(fp.getFetchGroups().contains("DescFetchGroup")); + { + FGManager findMgr = em.find(FGManager.class, mgr.getId()); + em.clear(); + + assertEquals(mgr.getId(), findMgr.getId()); + assertEquals(mgr.getFirstName(), findMgr.getFirstName()); + assertEquals(mgr.getLastName(), findMgr.getLastName()); + assertNull(findMgr.getDescription()); // Should be lazy-loaded + } + + em.close(); + emf2.close(); + } + + private void createEmployeeData() { + OpenJPAEntityManager em = emf.createEntityManager(); + + employeeSet.clear(); + managerSet.clear(); + + int empIdIndex = 1; + + em.getTransaction().begin(); + + // Create Managers + for (int i = 1; i < mgrCount; i++) { + int id = empIdIndex++; + + FGAddress addr = createAddress(id); + em.persist(addr); + + FGDepartment dept = createDepartment(id); + em.persist(dept); + + FGManager mgr = new FGManager(); + mgr.setId(id); + mgr.setFirstName("First-" + id); + mgr.setLastName("Last-" + id); + mgr.setMData("MData-" + id); + mgr.setRating("Rating-" + id); + mgr.setDescription("Manager-" + id); + mgr.setAddress(addr); + mgr.setDept(dept); + + em.persist(mgr); + + managerSet.add(mgr); + } + + // Create Employees + for (int i = 1; i < empCount; i++) { + int id = empIdIndex++; + int mgrId = (id % empPerMgr) + 1; + + FGAddress addr = createAddress(id); + em.persist(addr); + + FGDepartment dept = createDepartment(id); + em.persist(dept); + + FGEmployee emp = new FGEmployee(); + emp.setId(id); + emp.setFirstName("First-" + id); + emp.setLastName("Last-" + id); + emp.setRating("Rating-" + id); + emp.setDescription("Employee-" + id); + emp.setAddress(addr); + emp.setDept(dept); + + em.persist(emp); + + employeeSet.add(emp); + } + + em.getTransaction().commit(); + + em.close(); + } + + private FGAddress createAddress(int id) { + FGAddress addr = new FGAddress(); + addr.setId(id); + addr.setStreet("Street-" + id); + addr.setCity("City-" + id); + addr.setState("State-" + id); + addr.setZip(id); + + return addr; + } + + private FGDepartment createDepartment(int id) { + FGDepartment dept = new FGDepartment(); + dept.setId(id); + dept.setName("Department-" + id); + + return dept; + } +}