diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/EventCache.java b/hibernate-core/src/main/java/org/hibernate/event/internal/EventCache.java index 94263b3aad..9bdf9f47f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/EventCache.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/EventCache.java @@ -38,6 +38,14 @@ import org.hibernate.AssertionFailure; * to the EventCache before the operation has cascaded to that * entity. *

+ * There are some restriction; + *

+ *

* The following methods can be used by event listeners (and other * classes) in the same package to add entities to an EventCache * and indicate if the operation is being performed on the entity:

@@ -85,9 +93,9 @@ class EventCache implements Map { } /** - * Returns true if this EventCache maps one or more entities to the specified copy. + * Returns true if this EventCache maps an entity to the specified copy. * @param copy must be non-null - * @return true if this EventCache maps one or more entities to the specified copy + * @return true if this EventCache maps an entity to the specified copy * @throws NullPointerException if copy is null */ public boolean containsValue(Object copy) { @@ -141,10 +149,11 @@ class EventCache implements Map { /** * Associates the specified entity with the specified copy in this EventCache; * @param entity must be non-null - * @param copy must be non- null + * @param copy must be non- null and must not be associated with any other entity in this EntityCache. * @return previous copy associated with specified entity, or null if * there was no mapping for entity. * @throws NullPointerException if entity or copy is null + * @throws IllegalStateException if the specified copy is already associated with a different entity. */ public Object put(Object entity, Object copy) { return put( entity, copy, Boolean.FALSE ); @@ -153,12 +162,13 @@ class EventCache implements Map { /** * Associates the specified entity with the specified copy in this EventCache; * @param entity must be non-null - * @param copy must be non- null - * @param isOperatedOn indicates if the operation is performed on the entity + * @param copy must be non- null and must not be associated with any other entity in this EntityCache. + * @param isOperatedOn indicates if the operation is performed on the entity. * * @return previous copy associated with specified entity, or null if * there was no mapping for entity. * @throws NullPointerException if entity or copy is null + * @throws IllegalStateException if the specified copy is already associated with a different entity. */ /* package-private */ Object put(Object entity, Object copy, boolean isOperatedOn) { if ( entity == null || copy == null ) { @@ -171,25 +181,34 @@ class EventCache implements Map { if ( oldCopy == null ) { if ( oldEntity != null ) { - throw new IllegalStateException( "An entity copy is already assigned to a different entity." ); + throw new IllegalStateException( "An entity copy was already assigned to a different entity." ); } if ( oldOperatedOn != null ) { throw new IllegalStateException( "entityToOperatedOnFlagMap contains an entity, but entityToCopyMap does not." ); } } else { - if ( oldEntity == null ) { - throw new IllegalStateException( "An entity already had a copy in entityToCopyMap, but the specified copy was not in copyToEntityMap." ); + if ( oldCopy != copy ) { + // Replaced an entity copy with a new copy; need to remove the oldCopy from copyToEntityMap + // to synch things up. + Object removedEntity = copyToEntityMap.remove( oldCopy ); + if ( removedEntity != entity ) { + throw new IllegalStateException( "An unexpected entity was associated with the old entity copy." ); + } + if ( oldEntity != null ) { + throw new IllegalStateException( "A new entity copy is already associated with a different entity." ); + } + } + else { + // Replaced an entity copy with the same copy in entityToCopyMap. + // Make sure that copy is associated with the same entity in copyToEntityMap. + if ( oldEntity != entity ) { + throw new IllegalStateException( "An entity copy was associated with a different entity than provided." ); + } } if ( oldOperatedOn == null ) { throw new IllegalStateException( "entityToCopyMap contained an entity, but entityToOperatedOnFlagMap did not." ); } - if ( oldEntity != entity ) { - throw new IllegalStateException( "An entity copy was associated with a different entity than provided." ); - } - if ( oldCopy != copy ) { - throw new IllegalStateException( "An entity was already associated with a copy that is different from the copy provided." ); - } } return oldCopy; diff --git a/hibernate-core/src/test/java/org/hibernate/event/internal/EventCacheTest.java b/hibernate-core/src/test/java/org/hibernate/event/internal/EventCacheTest.java index b3186a43c5..780b6fd072 100644 --- a/hibernate-core/src/test/java/org/hibernate/event/internal/EventCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/event/internal/EventCacheTest.java @@ -319,24 +319,28 @@ public class EventCacheTest extends TestCase { assertSame( copy, entry.getValue() ); } - public void testReplaceCopyForEntity() { + public void testReplaceEntityCopy() { EventCache cache = new EventCache(); Simple entity = new Simple( 1 ); Simple copy = new Simple( 0 ); cache.put(entity, copy); - try { - cache.put( entity, new Simple( 0 ) ); - fail( "should have thrown IllegalStateException"); - } - catch( IllegalStateException ex ) { - // expected - } + Simple copyNew = new Simple( 0 ); + assertSame( copy, cache.put( entity, copyNew ) ); + assertSame( copyNew, cache.get( entity ) ); + checkCacheConsistency( cache, 1 ); + + copy = copyNew; + copyNew = new Simple( 1 ); + assertSame( copy, cache.put( entity, copyNew ) ); + assertSame( copyNew, cache.get( entity ) ); + + checkCacheConsistency( cache, 1 ); } - public void testReplaceEntityForCopy() { + public void testCopyAssociatedWithNewAndExistingEntity() { EventCache cache = new EventCache(); Simple entity = new Simple( 1 ); @@ -353,26 +357,7 @@ public class EventCacheTest extends TestCase { } - public void testReplaceEntityForExistingCopy() { - - EventCache cache = new EventCache(); - Simple entity1 = new Simple( 1 ); - Simple copy1 = new Simple( 0 ); - cache.put(entity1, copy1); - Simple entity2 = new Simple( 2 ); - Simple copy2 = new Simple( 0 ); - cache.put( entity2, copy2 ); - - try { - cache.put( entity1, copy2 ); - fail( "should have thrown IllegalStateException"); - } - catch( IllegalStateException ex ) { - // expected - } - } - - public void testReplaceCopyForExistingEntity() { + public void testCopyAssociatedWith2ExistingEntities() { EventCache cache = new EventCache(); Simple entity1 = new Simple( 1 );