Fix for bug 470161 - ServerSession links RepeatableWriteUnitOfWork via entity / IndirectList / QueryBasedValueHolder
- Added call to ValueHolder releaseWrappedValueHolder() in TransparentIndirectionPolicy getOriginalValueHolder() to release a wrapped valueholder when TransparentIndirectionPolicy is utilized
- Added test: testTransparentIndirectionValueHolderSessionReset to AdvancedJPAJunitTest
- LRG & JPA LRG on MySQL, Oracle = Pass
Reviewed-by: Gordon Yorke <gordon.yorke@oracle.com>
Reviewed-by: Lukas Jungmann <lukas.jungmann@oracle.com>
Signed-off-by: Lukas Jungmann <lukas.jungmann@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/indirection/TransparentIndirectionPolicy.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/indirection/TransparentIndirectionPolicy.java
index be9509a..4c8f0a4 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/indirection/TransparentIndirectionPolicy.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/indirection/TransparentIndirectionPolicy.java
@@ -362,6 +362,10 @@
valueHolder = ((WrappingValueHolder)valueHolder).getWrappedValueHolder();
}
}
+ // EL bug 470161
+ if ((valueHolder != null) && (valueHolder instanceof DatabaseValueHolder)) {
+ ((DatabaseValueHolder) valueHolder).releaseWrappedValueHolder(session);
+ }
return valueHolder;
} else {
return container.getValueHolder();
diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java
index f37f225..9f52460 100644
--- a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java
+++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java
@@ -63,10 +63,12 @@
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy;
import org.eclipse.persistence.descriptors.invalidation.TimeToLiveCacheInvalidationPolicy;
+import org.eclipse.persistence.indirection.IndirectCollection;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.indirection.DatabaseValueHolder;
import org.eclipse.persistence.history.AsOfClause;
import org.eclipse.persistence.internal.jpa.EJBQueryImpl;
import org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate;
@@ -237,6 +239,7 @@
suite.addTest(new AdvancedJPAJunitTest("testAttributeOverrideToMultipleSameDefaultColumnName"));
suite.addTest(new AdvancedJPAJunitTest("testJoinFetchWithRefreshOnRelatedEntity"));
suite.addTest(new AdvancedJPAJunitTest("testSharedEmbeddedAttributeOverrides"));
+ suite.addTest(new AdvancedJPAJunitTest("testTransparentIndirectionValueHolderSessionReset"));
if (!isJPA10()) {
// These tests use JPA 2.0 entity manager API
@@ -3215,6 +3218,91 @@
closeEntityManager(em);
}
}
+
+ /**
+ * Bug 470161 - ServerSession links RepeatableWriteUnitOfWork via entity / IndirectList / QueryBasedValueHolder\
+ *
+ * Through the UoW: Persist a new object, read an existing object in (building from rows, original not put in the
+ * shared cache), associate existing object with new object, commit. Previously, after the changes are merged
+ * into the shared cache, the transparent collection on the existing object has a wrapped VH from the initial uow
+ * query, which internally references the uow session, not the shared session.
+ */
+ public void testTransparentIndirectionValueHolderSessionReset() {
+ Employee emp = null;
+ Dealer dealer = null;
+
+ // setup
+ EntityManager em = createEntityManager();
+ try {
+ beginTransaction(em);
+ dealer = new Dealer();
+ dealer.setFirstName("Angle");
+ dealer.setLastName("Bracket");
+ em.persist(dealer);
+ commitTransaction(em);
+ } finally {
+ closeEntityManager(em);
+ clearCache(); // start test with an empty cache
+ }
+
+ // test
+ em = createEntityManager();
+ try {
+ beginTransaction(em);
+
+ emp = new Employee();
+ emp.setFemale();
+ emp.setFirstName("Case");
+ emp.setLastName("Statement");
+ em.persist(emp);
+
+ Query query = em.createQuery("select d from Dealer d where d.firstName = :firstName and d.lastName = :lastName");
+ query.setParameter("firstName", dealer.getFirstName());
+ query.setParameter("lastName", dealer.getLastName());
+ List<Dealer> resultsList = query.getResultList();
+ assertTrue("List returned should be non-empty", resultsList.size() > 0);
+
+ Dealer dealerFound = resultsList.get(0);
+ emp.addDealer(dealerFound);
+
+ commitTransaction(em);
+
+ // verify valueholder configuration in shared cache
+ Session parentSession = JpaHelper.getServerSession(em.getEntityManagerFactory());
+ Dealer cachedDealer = (Dealer) parentSession.getIdentityMapAccessor().getFromIdentityMap(dealer);
+ assertNotNull("Dealer with id should be in the cache: " + dealer.getId(), cachedDealer);
+
+ ClassDescriptor descriptor = parentSession.getDescriptor(Dealer.class);
+ DatabaseMapping mapping = descriptor.getMappingForAttributeName("customers");
+ IndirectCollection indirectCollection = (IndirectCollection) mapping.getAttributeValueFromObject(cachedDealer);
+ assertFalse("Collection VH should be uninstantiated", indirectCollection.isInstantiated());
+
+ DatabaseValueHolder dbValueHolder = (DatabaseValueHolder) indirectCollection.getValueHolder();
+ assertFalse("Referenced VH should be uninstantiated", dbValueHolder.isInstantiated());
+
+ Session vhSession = dbValueHolder.getSession();
+ assertSame("Dealer.customers VH session should reference the shared session", parentSession, vhSession);
+ } finally {
+ closeEntityManager(em);
+ }
+
+ // reset
+ em = createEntityManager();
+ try {
+ beginTransaction(em);
+ emp = em.find(Employee.class, emp.getId());
+ if (emp != null) {
+ em.remove(emp);
+ }
+ dealer = em.find(Dealer.class, dealer.getId());
+ if (dealer != null) {
+ em.remove(dealer);
+ }
+ commitTransaction(em);
+ } finally {
+ closeEntityManager(em);
+ }
+ }
protected int getVersion(EntityManager em, Dealer dealer) {
Vector pk = new Vector(1);