HHH-7928 - Regression caused by HHH-6361
This commit is contained in:
parent
6263bef63e
commit
cb1b9a05af
|
@ -264,6 +264,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.
|
||||
|
@ -273,9 +275,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) {
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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,8 +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 );
|
||||
Character paulo = new Character( 2, "Paulo Atreides" );
|
||||
s.persist( paulo );
|
||||
|
||||
Alias alias1 = new Alias( 1, "Paul Muad'Dib" );
|
||||
s.persist( alias1 );
|
||||
|
@ -73,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();
|
||||
|
||||
|
@ -86,17 +72,24 @@ public class BadMergeHandlingTest extends BaseCoreFunctionalTestCase {
|
|||
s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
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 );
|
||||
|
||||
cc1.customer = paul;
|
||||
s.merge( cc1 );
|
||||
cc2.customer = paul;
|
||||
s.merge( cc2 );
|
||||
s.flush();
|
||||
|
||||
// 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();
|
||||
|
@ -104,71 +97,12 @@ 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( 1, results.size() );
|
||||
assertEquals( 2, results.size() );
|
||||
s.getTransaction().commit();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
|
||||
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.internal.util.SerializationHelper;
|
||||
|
||||
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;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* @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, 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() );
|
||||
|
||||
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();
|
||||
for ( Identifiable element : snapshot ) {
|
||||
assertEquals( expectedOwner.getClass().getName(), event.getAffectedOwnerEntityName() );
|
||||
assertEquals( expectedOwner.getId(), event.getAffectedOwnerIdOrNull() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<T> copy(List<T> source) {
|
||||
ArrayList<T> copy = new ArrayList<T>( source.size() );
|
||||
copy.addAll( source );
|
||||
return copy;
|
||||
}
|
||||
}
|
|
@ -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 = "
|
||||
|
|
Loading…
Reference in New Issue