HHH-9261 : Backport HHH-9106 to 4.2 (Multiple representations of the same entity cannot be merged using cascade=merge)
This commit is contained in:
parent
5597111f32
commit
0e43f71ca0
|
@ -33,9 +33,7 @@ import org.hibernate.HibernateException;
|
||||||
import org.hibernate.ObjectDeletedException;
|
import org.hibernate.ObjectDeletedException;
|
||||||
import org.hibernate.StaleObjectStateException;
|
import org.hibernate.StaleObjectStateException;
|
||||||
import org.hibernate.WrongClassException;
|
import org.hibernate.WrongClassException;
|
||||||
import org.hibernate.boot.registry.selector.spi.StrategySelector;
|
|
||||||
import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor;
|
import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor;
|
||||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
|
||||||
import org.hibernate.engine.internal.Cascade;
|
import org.hibernate.engine.internal.Cascade;
|
||||||
import org.hibernate.engine.spi.CascadingAction;
|
import org.hibernate.engine.spi.CascadingAction;
|
||||||
import org.hibernate.engine.spi.EntityEntry;
|
import org.hibernate.engine.spi.EntityEntry;
|
||||||
|
@ -51,6 +49,9 @@ import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.proxy.LazyInitializer;
|
import org.hibernate.proxy.LazyInitializer;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
import org.hibernate.service.classloading.spi.ClassLoaderService;
|
||||||
|
import org.hibernate.service.classloading.spi.ClassLoadingException;
|
||||||
|
import org.hibernate.service.config.spi.ConfigurationService;
|
||||||
import org.hibernate.type.ForeignKeyDirection;
|
import org.hibernate.type.ForeignKeyDirection;
|
||||||
import org.hibernate.type.TypeHelper;
|
import org.hibernate.type.TypeHelper;
|
||||||
|
|
||||||
|
@ -64,8 +65,8 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
||||||
|
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
|
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
|
||||||
DefaultMergeEventListener.class.getName());
|
DefaultMergeEventListener.class.getName());
|
||||||
|
private static final String ENTITY_COPY_OBSERVER_STRATEGY_PROPERTY_NAME = "hibernate.event.merge.entity_copy_observer";
|
||||||
private String entityCopyObserverStrategy;
|
private Class<? extends EntityCopyObserver> entityCopyObserverClass;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Map getMergeMap(Object anything) {
|
protected Map getMergeMap(Object anything) {
|
||||||
|
@ -92,24 +93,18 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityCopyObserver createEntityCopyObserver(SessionFactoryImplementor sessionFactory) {
|
private EntityCopyObserver createEntityCopyObserver(SessionFactoryImplementor sessionFactory) {
|
||||||
final ServiceRegistry serviceRegistry = sessionFactory.getServiceRegistry();
|
if ( entityCopyObserverClass == null ) {
|
||||||
if ( entityCopyObserverStrategy == null ) {
|
entityCopyObserverClass = determineEntityCopyObserverClass( sessionFactory );
|
||||||
final ConfigurationService configurationService
|
}
|
||||||
= serviceRegistry.getService( ConfigurationService.class );
|
try {
|
||||||
entityCopyObserverStrategy = configurationService.getSetting(
|
return entityCopyObserverClass.newInstance();
|
||||||
"hibernate.event.merge.entity_copy_observer",
|
}
|
||||||
new ConfigurationService.Converter<String>() {
|
catch (Exception e) {
|
||||||
@Override
|
throw new HibernateException(
|
||||||
public String convert(Object value) {
|
String.format( "Could not instantiate EntityCopyObserver implementation [%s]", entityCopyObserverClass.getName() ),
|
||||||
return value.toString();
|
e
|
||||||
}
|
);
|
||||||
},
|
|
||||||
EntityCopyNotAllowedObserver.SHORT_NAME
|
|
||||||
);
|
|
||||||
LOG.debugf( "EntityCopyObserver strategy: %s", entityCopyObserverStrategy );
|
|
||||||
}
|
}
|
||||||
final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class );
|
|
||||||
return strategySelector.resolveStrategy( EntityCopyObserver.class, entityCopyObserverStrategy );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -504,4 +499,46 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
||||||
protected void cascadeBeforeSave(EventSource source, EntityPersister persister, Object entity, Object anything)
|
protected void cascadeBeforeSave(EventSource source, EntityPersister persister, Object entity, Object anything)
|
||||||
throws HibernateException {
|
throws HibernateException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Class<? extends EntityCopyObserver> determineEntityCopyObserverClass(
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
final ServiceRegistry serviceRegistry = sessionFactory.getServiceRegistry();
|
||||||
|
final ConfigurationService configurationService
|
||||||
|
= serviceRegistry.getService( ConfigurationService.class );
|
||||||
|
final String entityCopyObserverStrategyString = configurationService.getSetting(
|
||||||
|
ENTITY_COPY_OBSERVER_STRATEGY_PROPERTY_NAME,
|
||||||
|
new ConfigurationService.Converter<String>() {
|
||||||
|
@Override
|
||||||
|
public String convert(Object value) {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
EntityCopyNotAllowedObserver.SHORT_NAME
|
||||||
|
);
|
||||||
|
LOG.debugf( "EntityCopyObserver strategy: %s", entityCopyObserverStrategyString );
|
||||||
|
if ( EntityCopyNotAllowedObserver.SHORT_NAME.equals( entityCopyObserverStrategyString ) ) {
|
||||||
|
return EntityCopyNotAllowedObserver.class;
|
||||||
|
}
|
||||||
|
else if ( EntityCopyAllowedObserver.SHORT_NAME.equals( entityCopyObserverStrategyString ) ) {
|
||||||
|
return EntityCopyAllowedObserver.class;
|
||||||
|
}
|
||||||
|
else if ( EntityCopyAllowedLoggedObserver.SHORT_NAME.equals( entityCopyObserverStrategyString ) ) {
|
||||||
|
return EntityCopyAllowedLoggedObserver.class;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
|
||||||
|
try {
|
||||||
|
return classLoaderService.classForName( entityCopyObserverStrategyString );
|
||||||
|
}
|
||||||
|
catch (ClassLoadingException e) {
|
||||||
|
throw new HibernateException(
|
||||||
|
"Unable resolve strategy [" +
|
||||||
|
entityCopyObserverStrategyString +
|
||||||
|
"] as strategy for [" +
|
||||||
|
ENTITY_COPY_OBSERVER_STRATEGY_PROPERTY_NAME + "]",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014, 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.ejb.event;
|
|
||||||
|
|
||||||
import org.hibernate.event.internal.EntityCopyAllowedLoggedObserver;
|
|
||||||
import org.hibernate.event.internal.EntityCopyAllowedObserver;
|
|
||||||
import org.hibernate.event.internal.EntityCopyObserver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides {@link EJB3MergeEventListener} that allows merging multiple representations
|
|
||||||
* of the same persistent entity.
|
|
||||||
*
|
|
||||||
* @author Gail Badner
|
|
||||||
*/
|
|
||||||
public class EJB3EntityCopyAllowedMergeEventListener extends EJB3MergeEventListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected EntityCopyObserver createEntityCopyObserver() {
|
|
||||||
return EntityCopyAllowedLoggedObserver.isDebugLoggingEnabled() ?
|
|
||||||
new EntityCopyAllowedLoggedObserver() :
|
|
||||||
new EntityCopyAllowedObserver();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -21,7 +21,7 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.jpa.test.emops;
|
package org.hibernate.ejb.test.emops;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.jpa.test.emops;
|
package org.hibernate.ejb.test.emops;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -29,7 +29,7 @@ import javax.persistence.EntityManager;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
|
@ -21,7 +21,7 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.jpa.test.emops;
|
package org.hibernate.ejb.test.emops;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -33,7 +33,7 @@ import org.junit.Test;
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.event.spi.EntityCopyObserver;
|
import org.hibernate.event.spi.EntityCopyObserver;
|
||||||
import org.hibernate.event.spi.EventSource;
|
import org.hibernate.event.spi.EventSource;
|
||||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
|
||||||
import static junit.framework.TestCase.fail;
|
import static junit.framework.TestCase.fail;
|
|
@ -21,7 +21,7 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.jpa.test.emops;
|
package org.hibernate.ejb.test.emops;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ import javax.persistence.EntityManager;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
|
||||||
import static junit.framework.TestCase.fail;
|
import static junit.framework.TestCase.fail;
|
|
@ -21,7 +21,7 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.jpa.test.emops;
|
package org.hibernate.ejb.test.emops;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014, 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.ejb.test.emops;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
import org.hibernate.ejb.HibernateEntityManagerFactory;
|
|
||||||
import org.hibernate.ejb.event.EJB3EntityCopyAllowedMergeEventListener;
|
|
||||||
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
|
||||||
import org.hibernate.event.spi.EventType;
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertSame;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests merging multiple detached representations of the same entity using
|
|
||||||
* {@link org.hibernate.ejb.event.EJB3EntityCopyAllowedMergeEventListener}.
|
|
||||||
*
|
|
||||||
* @author Gail Badner
|
|
||||||
*/
|
|
||||||
@TestForIssue( jiraKey = "HHH-9106")
|
|
||||||
public class MergeMultipleEntityRepresentationsAllowedTest extends BaseEntityManagerFunctionalTestCase {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void afterEntityManagerFactoryBuilt() {
|
|
||||||
super.afterEntityManagerFactoryBuilt();
|
|
||||||
SessionFactory sf = ( (HibernateEntityManagerFactory) entityManagerFactory() ).getSessionFactory();
|
|
||||||
EventListenerRegistry registry =
|
|
||||||
( (SessionFactoryImplementor) sf ).getServiceRegistry().getService( EventListenerRegistry.class );
|
|
||||||
registry.setListeners( EventType.MERGE, new EJB3EntityCopyAllowedMergeEventListener() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCascadeFromDetachedToNonDirtyRepresentations() {
|
|
||||||
Item item1 = new Item();
|
|
||||||
item1.setName( "item1" );
|
|
||||||
|
|
||||||
Hoarder hoarder = new Hoarder();
|
|
||||||
hoarder.setName( "joe" );
|
|
||||||
|
|
||||||
EntityManager em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
em.persist( item1 );
|
|
||||||
em.persist( hoarder );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
// Get another representation of the same Item from a different EntityManager.
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
Item item1_1 = em.find( Item.class, item1.getId() );
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
// item1_1 and item1_2 are unmodified representations of the same persistent entity.
|
|
||||||
assertFalse( item1 == item1_1 );
|
|
||||||
assertTrue( item1.equals( item1_1 ) );
|
|
||||||
|
|
||||||
// Update hoarder (detached) to references both representations.
|
|
||||||
hoarder.getItems().add( item1 );
|
|
||||||
hoarder.setFavoriteItem( item1_1 );
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
hoarder = em.merge( hoarder );
|
|
||||||
assertEquals( 1, hoarder.getItems().size() );
|
|
||||||
assertSame( hoarder.getFavoriteItem(), hoarder.getItems().iterator().next() );
|
|
||||||
assertEquals( item1.getId(), hoarder.getFavoriteItem().getId() );
|
|
||||||
assertEquals( item1.getCategory(), hoarder.getFavoriteItem().getCategory() );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
hoarder = em.merge( hoarder );
|
|
||||||
assertEquals( 1, hoarder.getItems().size() );
|
|
||||||
assertSame( hoarder.getFavoriteItem(), hoarder.getItems().iterator().next() );
|
|
||||||
assertEquals( item1.getId(), hoarder.getFavoriteItem().getId() );
|
|
||||||
assertEquals( item1.getCategory(), hoarder.getFavoriteItem().getCategory() );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTopLevelManyToOneManagedNestedIsDetached() {
|
|
||||||
Item item1 = new Item();
|
|
||||||
item1.setName( "item1 name" );
|
|
||||||
Category category = new Category();
|
|
||||||
category.setName( "category" );
|
|
||||||
item1.setCategory( category );
|
|
||||||
category.setExampleItem( item1 );
|
|
||||||
|
|
||||||
EntityManager em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
em.persist( item1 );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
// get another representation of item1
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
Item item1_1 = em.find( Item.class, item1.getId() );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
Item item1Merged = em.merge( item1 );
|
|
||||||
|
|
||||||
item1Merged.setCategory( category );
|
|
||||||
category.setExampleItem( item1_1 );
|
|
||||||
|
|
||||||
// now item1Merged is managed and it has a nested detached item
|
|
||||||
em.merge( item1Merged );
|
|
||||||
assertEquals( category.getName(), item1Merged.getCategory().getName() );
|
|
||||||
assertSame( item1Merged, item1Merged.getCategory().getExampleItem() );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
item1 = em.find( Item.class, item1.getId() );
|
|
||||||
assertEquals( category.getName(), item1.getCategory().getName() );
|
|
||||||
assertSame( item1, item1.getCategory().getExampleItem() );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings( {"unchecked"})
|
|
||||||
private void cleanup() {
|
|
||||||
EntityManager em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
|
|
||||||
for ( Hoarder hoarder : (List<Hoarder>) em.createQuery( "from Hoarder" ).getResultList() ) {
|
|
||||||
hoarder.getItems().clear();
|
|
||||||
em.remove( hoarder );
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( Category category : (List<Category>) em.createQuery( "from Category" ).getResultList() ) {
|
|
||||||
if ( category.getExampleItem() != null ) {
|
|
||||||
category.setExampleItem( null );
|
|
||||||
em.remove( category );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( Item item : (List<Item>) em.createQuery( "from Item" ).getResultList() ) {
|
|
||||||
item.setCategory( null );
|
|
||||||
em.remove( item );
|
|
||||||
}
|
|
||||||
|
|
||||||
em.createQuery( "delete from Item" ).executeUpdate();
|
|
||||||
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class[] getAnnotatedClasses() {
|
|
||||||
return new Class[] {
|
|
||||||
Category.class,
|
|
||||||
Hoarder.class,
|
|
||||||
Item.class
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014, 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.ejb.test.emops;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.fail;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests merging multiple detached representations of the same entity using
|
|
||||||
* a the default MergeEventListener (that does not allow this).
|
|
||||||
*
|
|
||||||
* @author Gail Badner
|
|
||||||
*/
|
|
||||||
@TestForIssue( jiraKey = "HHH-9106")
|
|
||||||
public class MergeMultipleEntityRepresentationsNotAllowedTest extends BaseEntityManagerFunctionalTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCascadeFromDetachedToNonDirtyRepresentations() {
|
|
||||||
Item item1 = new Item();
|
|
||||||
item1.setName( "item1" );
|
|
||||||
|
|
||||||
Hoarder hoarder = new Hoarder();
|
|
||||||
hoarder.setName( "joe" );
|
|
||||||
|
|
||||||
EntityManager em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
em.persist( item1 );
|
|
||||||
em.persist( hoarder );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
// Get another representation of the same Item from a different session.
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
Item item1_1 = em.find( Item.class, item1.getId() );
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
// item1_1 and item1_2 are unmodified representations of the same persistent entity.
|
|
||||||
assertFalse( item1 == item1_1 );
|
|
||||||
assertTrue( item1.equals( item1_1 ) );
|
|
||||||
|
|
||||||
// Update hoarder (detached) to references both representations.
|
|
||||||
hoarder.getItems().add( item1 );
|
|
||||||
hoarder.setFavoriteItem( item1_1 );
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
try {
|
|
||||||
em.merge( hoarder );
|
|
||||||
fail( "should have failed due IllegalStateException");
|
|
||||||
}
|
|
||||||
catch (IllegalStateException ex) {
|
|
||||||
//expected
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
em.getTransaction().rollback();
|
|
||||||
em.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTopLevelManyToOneManagedNestedIsDetached() {
|
|
||||||
Item item1 = new Item();
|
|
||||||
item1.setName( "item1 name" );
|
|
||||||
Category category = new Category();
|
|
||||||
category.setName( "category" );
|
|
||||||
item1.setCategory( category );
|
|
||||||
category.setExampleItem( item1 );
|
|
||||||
|
|
||||||
EntityManager em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
em.persist( item1 );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
// get another representation of item1
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
Item item1_1 = em.find( Item.class, item1.getId() );
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
|
|
||||||
em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
Item item1Merged = em.merge( item1 );
|
|
||||||
|
|
||||||
item1Merged.setCategory( category );
|
|
||||||
category.setExampleItem( item1_1 );
|
|
||||||
|
|
||||||
// now item1Merged is managed and it has a nested detached item
|
|
||||||
try {
|
|
||||||
em.merge( item1Merged );
|
|
||||||
fail( "should have failed due IllegalStateException");
|
|
||||||
}
|
|
||||||
catch (IllegalStateException ex) {
|
|
||||||
//expected
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
em.getTransaction().rollback();
|
|
||||||
em.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings( {"unchecked"})
|
|
||||||
private void cleanup() {
|
|
||||||
EntityManager em = getOrCreateEntityManager();
|
|
||||||
em.getTransaction().begin();
|
|
||||||
|
|
||||||
for ( Hoarder hoarder : (List<Hoarder>) em.createQuery( "from Hoarder" ).getResultList() ) {
|
|
||||||
hoarder.getItems().clear();
|
|
||||||
em.remove( hoarder );
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( Category category : (List<Category>) em.createQuery( "from Category" ).getResultList() ) {
|
|
||||||
if ( category.getExampleItem() != null ) {
|
|
||||||
category.setExampleItem( null );
|
|
||||||
em.remove( category );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( Item item : (List<Item>) em.createQuery( "from Item" ).getResultList() ) {
|
|
||||||
item.setCategory( null );
|
|
||||||
em.remove( item );
|
|
||||||
}
|
|
||||||
|
|
||||||
em.createQuery( "delete from Item" ).executeUpdate();
|
|
||||||
|
|
||||||
em.getTransaction().commit();
|
|
||||||
em.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class[] getAnnotatedClasses() {
|
|
||||||
return new Class[] {
|
|
||||||
Category.class,
|
|
||||||
Hoarder.class,
|
|
||||||
Item.class
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue