HHH-7928 - Regression caused by HHH-6361

(cherry picked from commit cb1b9a05af)

Conflicts:

	hibernate-core/src/test/java/org/hibernate/test/event/collection/detached/BadMergeHandlingTest.java
This commit is contained in:
Steve Ebersole 2013-01-24 11:59:40 -06:00
parent 603682937c
commit 61f548db6f
9 changed files with 704 additions and 131 deletions

View File

@ -267,6 +267,8 @@ public final class CollectionEntry implements Serializable {
return snapshot;
}
private boolean fromMerge = false;
/**
* Reset the stored snapshot for both the persistent collection and this collection entry.
* Used during the merge of detached collections.
@ -276,9 +278,14 @@ public final class CollectionEntry implements Serializable {
*/
public void resetStoredSnapshot(PersistentCollection collection, Serializable storedSnapshot) {
LOG.debugf("Reset storedSnapshot to %s for %s", storedSnapshot, this);
if ( fromMerge ) {
return; // EARLY EXIT!
}
snapshot = storedSnapshot;
collection.setSnapshot(loadedKey, role, snapshot);
collection.setSnapshot( loadedKey, role, snapshot );
fromMerge = true;
}
private void setLoadedPersister(CollectionPersister persister) {

View File

@ -526,15 +526,13 @@ public abstract class CollectionType extends AbstractType implements Association
// on the target because we simply do not know...
if ( original instanceof PersistentCollection ) {
if ( result instanceof PersistentCollection ) {
if ( ! ( ( PersistentCollection ) original ).isDirty() ) {
( ( PersistentCollection ) result ).clearDirty();
}
final PersistentCollection originalPersistentCollection = (PersistentCollection) original;
final PersistentCollection resultPersistentCollection = (PersistentCollection) result;
if ( elemType instanceof AssociationType ) {
preserveSnapshot( (PersistentCollection) original,
(PersistentCollection) result,
(AssociationType) elemType, owner, copyCache,
session );
preserveSnapshot( originalPersistentCollection, resultPersistentCollection, elemType, owner, copyCache, session );
if ( ! originalPersistentCollection.isDirty() ) {
resultPersistentCollection.clearDirty();
}
}
}
@ -542,9 +540,13 @@ public abstract class CollectionType extends AbstractType implements Association
return result;
}
private void preserveSnapshot(PersistentCollection original,
PersistentCollection result, AssociationType elemType,
Object owner, Map copyCache, SessionImplementor session) {
private void preserveSnapshot(
PersistentCollection original,
PersistentCollection result,
Type elemType,
Object owner,
Map copyCache,
SessionImplementor session) {
Serializable originalSnapshot = original.getStoredSnapshot();
Serializable resultSnapshot = result.getStoredSnapshot();
Serializable targetSnapshot;
@ -553,39 +555,35 @@ public abstract class CollectionType extends AbstractType implements Association
targetSnapshot = new ArrayList(
( (List) originalSnapshot ).size() );
for ( Object obj : (List) originalSnapshot ) {
( (List) targetSnapshot ).add( elemType.replace(
obj, null, session, owner, copyCache ) );
( (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() );
targetSnapshot = new TreeMap( ( (SortedMap) originalSnapshot ).comparator() );
}
else {
targetSnapshot = new HashMap(
CollectionHelper.determineProperSizing(
( (Map) originalSnapshot ).size() ),
CollectionHelper.LOAD_FACTOR );
CollectionHelper.determineProperSizing( ( (Map) originalSnapshot ).size() ),
CollectionHelper.LOAD_FACTOR
);
}
for ( Map.Entry<Object, Object> entry : (
(Map<Object, Object>) originalSnapshot ).entrySet() ) {
for ( Map.Entry<Object, Object> entry : ( (Map<Object, Object>) originalSnapshot ).entrySet() ) {
Object key = entry.getKey();
Object value = entry.getValue();
Object resultSnapshotValue = ( resultSnapshot == null ) ? null
Object resultSnapshotValue = ( resultSnapshot == null )
? null
: ( (Map<Object, Object>) resultSnapshot ).get( key );
Object newValue = elemType.replace( value, resultSnapshotValue, session, owner, copyCache );
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 );
}
@ -595,8 +593,7 @@ public abstract class CollectionType extends AbstractType implements Association
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 );
arr[i] = elemType.replace( arr[i], null, session, owner, copyCache );
}
targetSnapshot = originalSnapshot;
@ -607,8 +604,7 @@ public abstract class CollectionType extends AbstractType implements Association
}
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(
result );
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( result );
if ( ce != null ) {
ce.resetStoredSnapshot( result, targetSnapshot );
}

View File

@ -0,0 +1,202 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.event.collection.detached;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.AbstractCollectionEvent;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.InitializeCollectionEvent;
import org.hibernate.event.spi.InitializeCollectionEventListener;
import org.hibernate.event.spi.PostCollectionRecreateEvent;
import org.hibernate.event.spi.PostCollectionRecreateEventListener;
import org.hibernate.event.spi.PostCollectionRemoveEvent;
import org.hibernate.event.spi.PostCollectionRemoveEventListener;
import org.hibernate.event.spi.PostCollectionUpdateEvent;
import org.hibernate.event.spi.PostCollectionUpdateEventListener;
import org.hibernate.event.spi.PreCollectionRecreateEvent;
import org.hibernate.event.spi.PreCollectionRecreateEventListener;
import org.hibernate.event.spi.PreCollectionRemoveEvent;
import org.hibernate.event.spi.PreCollectionRemoveEventListener;
import org.hibernate.event.spi.PreCollectionUpdateEvent;
import org.hibernate.event.spi.PreCollectionUpdateEventListener;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/**
* @author Steve Ebersole
* @author Gail Badner
*/
public class AggregatedCollectionEventListener
implements InitializeCollectionEventListener,
PreCollectionRecreateEventListener,
PostCollectionRecreateEventListener,
PreCollectionRemoveEventListener,
PostCollectionRemoveEventListener,
PreCollectionUpdateEventListener,
PostCollectionUpdateEventListener {
private static final Logger log = Logger.getLogger( AggregatedCollectionEventListener.class );
private final List<EventEntry> eventEntryList = new ArrayList<EventEntry>();
public void reset() {
eventEntryList.clear();
}
public List<EventEntry> getEventEntryList() {
return eventEntryList;
}
@Override
public void onInitializeCollection(InitializeCollectionEvent event) throws HibernateException {
addEvent( event );
}
protected void addEvent(AbstractCollectionEvent event) {
log.debugf( "Added collection event : %s", event );
eventEntryList.add( new EventEntry( event ) );
}
// recreate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void onPreRecreateCollection(PreCollectionRecreateEvent event) {
addEvent( event );
}
@Override
public void onPostRecreateCollection(PostCollectionRecreateEvent event) {
addEvent( event );
}
// remove ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void onPreRemoveCollection(PreCollectionRemoveEvent event) {
addEvent( event );
}
@Override
public void onPostRemoveCollection(PostCollectionRemoveEvent event) {
addEvent( event );
}
// update ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void onPreUpdateCollection(PreCollectionUpdateEvent event) {
addEvent( event );
}
@Override
public void onPostUpdateCollection(PostCollectionUpdateEvent event) {
addEvent( event );
}
public static class EventEntry {
private final AbstractCollectionEvent event;
private final Serializable snapshotAtTimeOfEventHandling;
public EventEntry(AbstractCollectionEvent event) {
this.event = event;
// make a copy of the collection?
this.snapshotAtTimeOfEventHandling = event.getSession()
.getPersistenceContext()
.getCollectionEntry( event.getCollection() )
.getSnapshot();
}
public AbstractCollectionEvent getEvent() {
return event;
}
public Serializable getSnapshotAtTimeOfEventHandling() {
return snapshotAtTimeOfEventHandling;
}
}
public static class IntegratorImpl implements Integrator {
private AggregatedCollectionEventListener listener;
public AggregatedCollectionEventListener getListener() {
if ( listener == null ) {
throw new HibernateException( "Integrator not yet processed" );
}
return listener;
}
@Override
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
integrate( serviceRegistry );
}
protected void integrate(SessionFactoryServiceRegistry serviceRegistry) {
if ( listener != null ) {
log.warn( "integrate called second time on testing collection listener Integrator (could be result of rebuilding SF on test failure)" );
}
listener = new AggregatedCollectionEventListener();
final EventListenerRegistry listenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
listenerRegistry.appendListeners( EventType.INIT_COLLECTION, listener );
listenerRegistry.appendListeners( EventType.PRE_COLLECTION_RECREATE, listener );
listenerRegistry.appendListeners( EventType.POST_COLLECTION_RECREATE, listener );
listenerRegistry.appendListeners( EventType.PRE_COLLECTION_REMOVE, listener );
listenerRegistry.appendListeners( EventType.POST_COLLECTION_REMOVE, listener );
listenerRegistry.appendListeners( EventType.PRE_COLLECTION_UPDATE, listener );
listenerRegistry.appendListeners( EventType.POST_COLLECTION_UPDATE, listener );
}
@Override
public void integrate(
MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
integrate( serviceRegistry );
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
}

View File

@ -0,0 +1,85 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.event.collection.detached;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import java.util.ArrayList;
import java.util.List;
/**
* @author Steve Ebersole
*/
@Entity
public class Alias implements Identifiable {
private Integer id;
private String alias;
private List<Character> characters = new ArrayList<Character>();
public Alias() {
}
public Alias(Integer id, String alias) {
this.id = id;
this.alias = alias;
}
@Id
@Override
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
@ManyToMany( cascade = CascadeType.ALL )
@JoinTable( name = "CHARACTER_ALIAS" )
// @JoinTable(
// name = "CHARACTER_ALIAS",
// joinColumns = @JoinColumn(name="ALIAS_ID", referencedColumnName="ID"),
// inverseJoinColumns = @JoinColumn(name="CHARACTER_ID", referencedColumnName="ID")
// )
public List<Character> getCharacters() {
return characters;
}
public void setCharacters(List<Character> characters) {
this.characters = characters;
}
}

View File

@ -23,17 +23,6 @@
*/
package org.hibernate.test.event.collection.detached;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.hibernate.Session;
@ -53,7 +42,7 @@ public class BadMergeHandlingTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Customer.class, Alias.class, CreditCard.class };
return new Class[] { Character.class, Alias.class };
}
@Test
@ -61,10 +50,11 @@ public class BadMergeHandlingTest extends BaseCoreFunctionalTestCase {
public void testMergeAndHold() {
Session s = openSession();
s.beginTransaction();
Customer paul = new Customer( 1, "Paul Atreides" );
Character paul = new Character( 1, "Paul Atreides" );
s.persist( paul );
Customer duke = new Customer( 2, "Duke Leto" );
s.persist( duke );
Character paulo = new Character( 2, "Paulo Atreides" );
s.persist( paulo );
Alias alias1 = new Alias( 1, "Paul Muad'Dib" );
s.persist( alias1 );
@ -75,12 +65,6 @@ public class BadMergeHandlingTest extends BaseCoreFunctionalTestCase {
Alias alias3 = new Alias( 3, "The Preacher" );
s.persist( alias3 );
CreditCard cc1 = new CreditCard( 1 );
s.persist( cc1 );
CreditCard cc2 = new CreditCard( 2 );
s.persist( cc2 );
s.getTransaction().commit();
s.close();
@ -88,29 +72,24 @@ public class BadMergeHandlingTest extends BaseCoreFunctionalTestCase {
s = openSession();
s.beginTransaction();
// customer 1
alias1.customers.add( paul );
// customer 1
alias1.getCharacters().add( paul );
s.merge( alias1 );
alias2.customers.add( paul );
alias2.getCharacters().add( paul );
s.merge( alias2 );
alias3.customers.add( paul );
alias3.getCharacters().add( paul );
s.merge( alias3 );
s.flush();
s.flush();
// customer 2
alias1.customers.add( duke );
s.merge( alias1 );
alias2.customers.add( duke );
s.merge( alias2 );
alias3.customers.add( duke );
s.merge( alias3 );
s.flush();
cc1.customer = paul;
s.merge( cc1 );
cc2.customer = paul;
s.merge( cc2 );
// customer 2
alias1.getCharacters().add( paulo );
s.merge( alias1 );
alias2.getCharacters().add( paulo );
s.merge( alias2 );
alias3.getCharacters().add( paulo );
s.merge( alias3 );
s.flush();
s.getTransaction().commit();
s.close();
@ -118,7 +97,7 @@ public class BadMergeHandlingTest extends BaseCoreFunctionalTestCase {
// now try to read them back (I guess)
s = openSession();
s.beginTransaction();
List results = s.createQuery( "select c from Customer c join c.aliases a where a.alias = :aParam" )
List results = s.createQuery( "select c from Character c join c.aliases a where a.alias = :aParam" )
.setParameter( "aParam", "Usul" )
.list();
assertEquals( 2, results.size() );
@ -126,63 +105,4 @@ public class BadMergeHandlingTest extends BaseCoreFunctionalTestCase {
s.close();
}
@Entity( name="Customer" )
public static class Customer {
@Id
private Integer id;
private String name;
@ManyToMany( cascade= CascadeType.ALL, mappedBy="customers" )
private Collection<Alias> aliases = new ArrayList<Alias>();
@OneToMany(cascade=CascadeType.ALL, mappedBy="customer")
private Collection<CreditCard> creditCards = new ArrayList<CreditCard>();
public Customer() {
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
}
@Entity( name="Alias" )
public static class Alias {
@Id
private Integer id;
private String alias;
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name="FKS_ALIAS_CUSTOMER",
joinColumns=
@JoinColumn(
name="FK_FOR_ALIAS_TABLE", referencedColumnName="ID"),
inverseJoinColumns=
@JoinColumn(
name="FK_FOR_CUSTOMER_TABLE", referencedColumnName="ID")
)
private Collection<Customer> customers = new ArrayList<Customer>();
public Alias() {
}
public Alias(Integer id, String alias) {
this.id = id;
this.alias = alias;
}
}
@Entity( name="CreditCard" )
public static class CreditCard {
@Id
private Integer id;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn (name="FK3_FOR_CUSTOMER_TABLE")
private Customer customer;
public CreditCard() {
}
public CreditCard(Integer id) {
this.id = id;
}
}
}

View File

@ -0,0 +1,81 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.event.collection.detached;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import java.util.ArrayList;
import java.util.List;
/**
* @author Steve Ebersole
*/
@Entity
public class Character implements Identifiable {
private Integer id;
private String name;
private List<Alias> aliases = new ArrayList<Alias>();
public Character() {
}
public Character(Integer id, String name) {
this.id = id;
this.name = name;
}
@Id
@Override
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany( cascade= CascadeType.ALL, mappedBy="characters" )
public List<Alias> getAliases() {
return aliases;
}
public void setAliases(List<Alias> aliases) {
this.aliases = aliases;
}
public void associateAlias(Alias alias) {
alias.getCharacters().add( this );
getAliases().add( alias );
}
}

View File

@ -0,0 +1,31 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.event.collection.detached;
/**
* @author Steve Ebersole
*/
public interface Identifiable {
public Integer getId();
}

View File

@ -0,0 +1,251 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.event.collection.detached;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.event.spi.AbstractCollectionEvent;
import org.hibernate.event.spi.PostCollectionRecreateEvent;
import org.hibernate.event.spi.PostCollectionUpdateEvent;
import org.hibernate.event.spi.PreCollectionRecreateEvent;
import org.hibernate.event.spi.PreCollectionRemoveEvent;
import org.hibernate.event.spi.PreCollectionUpdateEvent;
import org.hibernate.service.BootstrapServiceRegistryBuilder;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
/**
* @author Steve Ebersole
*/
@TestForIssue( jiraKey = "HHH-7928" )
public class MergeCollectionEventTest extends BaseCoreFunctionalTestCase {
private AggregatedCollectionEventListener.IntegratorImpl collectionListenerIntegrator =
new AggregatedCollectionEventListener.IntegratorImpl();
@Before
public void resetListener() {
collectionListenerIntegrator.getListener().reset();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Character.class, Alias.class };
}
@Override
protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) {
super.prepareBootstrapRegistryBuilder( builder );
builder.with( collectionListenerIntegrator );
}
@Override
protected void cleanupTestData() throws Exception {
Session s = openSession();
s.beginTransaction();
List<Alias> aliases = s.createQuery( "from Alias" ).list();
for ( Alias alias : aliases ) {
for ( Character character : alias.getCharacters() ) {
character.getAliases().clear();
}
alias.getCharacters().clear();
}
s.flush();
s.createQuery( "delete Alias" ).executeUpdate();
s.createQuery( "delete Character" ).executeUpdate();
s.getTransaction().commit();
s.close();
}
@Test
public void testCollectionEventHandlingOnMerge() {
final AggregatedCollectionEventListener listener = collectionListenerIntegrator.getListener();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This first bit really is just preparing the entities. There is generally no collection
// events of real interest during this part
Session s = openSession();
s.beginTransaction();
Character paul = new Character( 1, "Paul Atreides" );
s.save( paul );
s.getTransaction().commit();
s.close();
assertEquals( 2, listener.getEventEntryList().size() );
checkListener( 0, PreCollectionRecreateEvent.class, paul, Collections.EMPTY_LIST );
checkListener( 1, PostCollectionRecreateEvent.class, paul, Collections.EMPTY_LIST );
listener.reset();
s = openSession();
s.beginTransaction();
Character paulo = new Character( 2, "Paulo Atreides" );
s.save( paulo );
s.getTransaction().commit();
s.close();
assertEquals( 2, listener.getEventEntryList().size() );
checkListener( 0, PreCollectionRecreateEvent.class, paulo, Collections.EMPTY_LIST );
checkListener( 1, PostCollectionRecreateEvent.class, paulo, Collections.EMPTY_LIST );
listener.reset();
s = openSession();
s.beginTransaction();
Alias alias1 = new Alias( 1, "Paul Muad'Dib" );
s.save( alias1 );
s.getTransaction().commit();
s.close();
assertEquals( 2, listener.getEventEntryList().size() );
checkListener( 0, PreCollectionRecreateEvent.class, alias1, Collections.EMPTY_LIST );
checkListener( 1, PostCollectionRecreateEvent.class, alias1, Collections.EMPTY_LIST );
listener.reset();
s = openSession();
s.beginTransaction();
Alias alias2 = new Alias( 2, "Usul" );
s.save( alias2 );
s.getTransaction().commit();
s.close();
assertEquals( 2, listener.getEventEntryList().size() );
checkListener( 0, PreCollectionRecreateEvent.class, alias2, Collections.EMPTY_LIST );
checkListener( 1, PostCollectionRecreateEvent.class, alias2, Collections.EMPTY_LIST );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// at this point we can start setting up the associations and checking collection events
// of "real interest"
listener.reset();
paul.associateAlias( alias1 );
paul.associateAlias( alias2 );
paulo.associateAlias( alias1 );
paulo.associateAlias( alias2 );
s = openSession();
s.beginTransaction();
s.merge( alias1 );
assertEquals( 0, listener.getEventEntryList().size() );
// this is where HHH-7928 (problem with HHH-6361 fix) shows up...
s.flush();
assertEquals( 8, listener.getEventEntryList().size() ); // 4 collections x 2 events per
checkListener( 0, PreCollectionUpdateEvent.class, alias1, Collections.EMPTY_LIST );
checkListener( 1, PostCollectionUpdateEvent.class, alias1, alias1.getCharacters() );
checkListener( 2, PreCollectionUpdateEvent.class, paul, Collections.EMPTY_LIST );
checkListener( 3, PostCollectionUpdateEvent.class, paul, paul.getAliases() );
checkListener( 4, PreCollectionUpdateEvent.class, alias2, Collections.EMPTY_LIST );
checkListener( 5, PostCollectionUpdateEvent.class, alias2, alias2.getCharacters() );
checkListener( 6, PreCollectionUpdateEvent.class, paulo, Collections.EMPTY_LIST );
checkListener( 7, PostCollectionUpdateEvent.class, paulo, paul.getAliases() );
List<Character> alias1CharactersSnapshot = copy( alias1.getCharacters() );
List<Character> alias2CharactersSnapshot = copy( alias2.getCharacters() );
listener.reset();
s.merge( alias2 );
assertEquals( 0, listener.getEventEntryList().size() );
s.flush();
assertEquals( 8, listener.getEventEntryList().size() ); // 4 collections x 2 events per
checkListener( 0, PreCollectionUpdateEvent.class, alias1, alias1CharactersSnapshot );
checkListener( 1, PostCollectionUpdateEvent.class, alias1, alias1CharactersSnapshot );
// checkListener( 2, PreCollectionUpdateEvent.class, paul, Collections.EMPTY_LIST );
// checkListener( 3, PostCollectionUpdateEvent.class, paul, paul.getAliases() );
checkListener( 4, PreCollectionUpdateEvent.class, alias2, alias2CharactersSnapshot );
checkListener( 5, PostCollectionUpdateEvent.class, alias2, alias2.getCharacters() );
// checkListener( 6, PreCollectionUpdateEvent.class, paulo, Collections.EMPTY_LIST );
// checkListener( 7, PostCollectionUpdateEvent.class, paulo, paul.getAliases() );
s.getTransaction().commit();
s.close();
//
// checkListener(listeners, listeners.getInitializeCollectionListener(),
// mce, null, eventCount++);
// checkListener(listeners, listeners.getPreCollectionUpdateListener(),
// mce, oldRefentities1, eventCount++);
// checkListener(listeners, listeners.getPostCollectionUpdateListener(),
// mce, mce.getRefEntities1(), eventCount++);
}
protected void checkListener(
int eventIndex,
Class<? extends AbstractCollectionEvent> expectedEventType,
Identifiable expectedOwner,
List<? extends Identifiable> expectedCollectionEntrySnapshot) {
final AggregatedCollectionEventListener.EventEntry eventEntry
= collectionListenerIntegrator.getListener().getEventEntryList().get( eventIndex );
final AbstractCollectionEvent event = eventEntry.getEvent();
assertTyping( expectedEventType, event );
// because of the merge graphs, the instances are likely different. just base check on type and id
// assertEquals( expectedOwner, event.getAffectedOwnerOrNull() );
assertEquals( expectedOwner.getClass().getName(), event.getAffectedOwnerEntityName() );
assertEquals( expectedOwner.getId(), event.getAffectedOwnerIdOrNull() );
if ( event instanceof PreCollectionUpdateEvent
|| event instanceof PreCollectionRemoveEvent
|| event instanceof PostCollectionRecreateEvent ) {
List<Identifiable> snapshot = (List) eventEntry.getSnapshotAtTimeOfEventHandling();
assertEquals( expectedCollectionEntrySnapshot.size(), snapshot.size() );
for ( int i = 0; i < expectedCollectionEntrySnapshot.size(); i++ ) {
Identifiable expected = expectedCollectionEntrySnapshot.get( i );
Identifiable found = snapshot.get( i );
assertEquals( expected.getClass().getName(), found.getClass().getName() );
assertEquals( expected.getId(), found.getId() );
}
}
}
private <T> List<T> copy(List<T> source) {
ArrayList<T> copy = new ArrayList<T>( source.size() );
copy.addAll( source );
return copy;
}
}

View File

@ -203,10 +203,10 @@ public class MultipleCollectionListeners {
public void addEvent(AbstractCollectionEvent event, Listener listener) {
CollectionEntry collectionEntry = event.getSession()
.getPersistenceContext()
.getCollectionEntry(event.getCollection());
Serializable snapshot = collectionEntry.getSnapshot();
log.debug("add Event: " + event.getClass() + "; listener = "