/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ 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.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.cfg.Configuration; 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.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Before; import org.junit.Test; 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 { @Override protected void configure(Configuration configuration) { super.configure( configuration ); configuration.setImplicitNamingStrategy( ImplicitNamingStrategyLegacyJpaImpl.INSTANCE ); } 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.applyIntegrator( 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; } }