From 7bb43baf0b0a3def006fe625a3c349bd0138bd56 Mon Sep 17 00:00:00 2001 From: Erik-Berndt Scheper Date: Mon, 29 Oct 2012 17:46:00 -0400 Subject: [PATCH] HHH-6361: Patch ensuring that collection events have the correct stored snapshot after merging a detached entity into the persistencecontext --- .../hibernate/engine/spi/CollectionEntry.java | 16 ++++- .../org/hibernate/type/CollectionType.java | 66 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java index 01d9ccd05e..fcd269acba 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java @@ -264,6 +264,20 @@ public final class CollectionEntry implements Serializable { return snapshot; } + /** + * Reset the stored snapshot for both the persistent collection and this collection entry. + * Used during the merge of detached collections. + * + * @param collection the persistentcollection to be updated + * @param storedSnapshot the new stored snapshot + */ + public void resetStoredSnapshot(PersistentCollection collection, Serializable storedSnapshot) { + LOG.debugf("Reset storedSnapshot to %s for %s", storedSnapshot, this); + + snapshot = storedSnapshot; + collection.setSnapshot(loadedKey, role, snapshot); + } + private void setLoadedPersister(CollectionPersister persister) { loadedPersister = persister; setRole( persister == null ? null : persister.getRole() ); @@ -422,4 +436,4 @@ public final class CollectionEntry implements Serializable { ( session == null ? null : session.getFactory() ) ); } -} \ No newline at end of file +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java b/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java index f7683161de..ebd9884734 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java @@ -29,9 +29,12 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; import org.dom4j.Element; import org.dom4j.Node; @@ -40,6 +43,7 @@ import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.Mapping; @@ -49,6 +53,7 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.MarkerObject; import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.relational.Size; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; @@ -512,12 +517,73 @@ public abstract class CollectionType extends AbstractType implements Association if ( ! ( ( PersistentCollection ) original ).isDirty() ) { ( ( PersistentCollection ) result ).clearDirty(); } + + if (elemType instanceof AssociationType) { + preserveSnapshot(( PersistentCollection )original, ( PersistentCollection ) result, (AssociationType) elemType, owner, copyCache, session); + } } } return result; } + private void preserveSnapshot(PersistentCollection original ,PersistentCollection result, AssociationType elemType, Object owner, + Map copyCache, SessionImplementor session) { + Serializable originalSnapshot = original.getStoredSnapshot(); + Serializable resultSnapshot = result.getStoredSnapshot(); + Serializable targetSnapshot; + + if (originalSnapshot instanceof List) { + targetSnapshot = new ArrayList(((List) originalSnapshot).size()); + for (Object obj : (List) originalSnapshot) { + ((List) targetSnapshot).add(elemType.replace(obj, null, session, owner, copyCache)); + } + + } else if (originalSnapshot instanceof Map) { + if (originalSnapshot instanceof SortedMap) { + targetSnapshot = new TreeMap(((SortedMap) originalSnapshot).comparator()); + } else { + targetSnapshot = new HashMap( + CollectionHelper.determineProperSizing( ((Map) originalSnapshot).size()), + CollectionHelper.LOAD_FACTOR); + } + + for (Map.Entry entry : ((Map) originalSnapshot).entrySet()) { + Object key = entry.getKey(); + Object value = entry.getValue(); + Object resultSnapshotValue = (resultSnapshot == null) ? null : ((Map) resultSnapshot).get(key); + + if (key == value) { + Object newValue = elemType.replace(value, resultSnapshotValue, session, owner, copyCache ); + ((Map) targetSnapshot).put(newValue, newValue); + + } else { + Object newValue = elemType.replace(value, resultSnapshotValue, session, owner, copyCache ); + ((Map) targetSnapshot).put(key, newValue); + } + + } + + } else if (originalSnapshot instanceof Object []) { + Object [] arr = ( Object []) originalSnapshot; + for (int i=0; i< arr.length; i++) { + arr[i] = elemType.replace(arr[i], null, session, owner, copyCache ); + } + targetSnapshot = originalSnapshot; + + } else { + // retain the same snapshot + targetSnapshot = resultSnapshot; + + } + + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(result); + if (ce != null) { + ce.resetStoredSnapshot(result, targetSnapshot); + } + + } + /** * Instantiate a new "underlying" collection exhibiting the same capacity * charactersitcs and the passed "original".