HHH-9106 : Merging multiple representations of the same entity (using StrategySelector)
(cherry picked from commit 6203b5da30
)
This commit is contained in:
parent
b0a2ae9d66
commit
1472681dc2
|
@ -93,6 +93,10 @@ import org.hibernate.engine.transaction.jta.platform.internal.WebSphereJtaPlatfo
|
|||
import org.hibernate.engine.transaction.jta.platform.internal.WeblogicJtaPlatform;
|
||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||
import org.hibernate.engine.transaction.spi.TransactionFactory;
|
||||
import org.hibernate.event.internal.EntityCopyAllowedLoggedObserver;
|
||||
import org.hibernate.event.internal.EntityCopyAllowedObserver;
|
||||
import org.hibernate.event.internal.EntityCopyNotAllowedObserver;
|
||||
import org.hibernate.event.spi.EntityCopyObserver;
|
||||
import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.PersistentTableBulkIdStrategy;
|
||||
import org.hibernate.hql.spi.TemporaryTableBulkIdStrategy;
|
||||
|
@ -163,6 +167,7 @@ public class StrategySelectorBuilder {
|
|||
addJtaPlatforms( strategySelector );
|
||||
addTransactionFactories( strategySelector );
|
||||
addMultiTableBulkIdStrategies( strategySelector );
|
||||
addEntityCopyObserverStrategies( strategySelector );
|
||||
|
||||
// apply auto-discovered registrations
|
||||
for ( StrategyRegistrationProvider provider : classLoaderService.loadJavaServices( StrategyRegistrationProvider.class ) ) {
|
||||
|
@ -373,4 +378,22 @@ public class StrategySelectorBuilder {
|
|||
TemporaryTableBulkIdStrategy.class
|
||||
);
|
||||
}
|
||||
|
||||
private void addEntityCopyObserverStrategies(StrategySelectorImpl strategySelector) {
|
||||
strategySelector.registerStrategyImplementor(
|
||||
EntityCopyObserver.class,
|
||||
EntityCopyNotAllowedObserver.SHORT_NAME,
|
||||
EntityCopyNotAllowedObserver.class
|
||||
);
|
||||
strategySelector.registerStrategyImplementor(
|
||||
EntityCopyObserver.class,
|
||||
EntityCopyAllowedObserver.SHORT_NAME,
|
||||
EntityCopyAllowedObserver.class
|
||||
);
|
||||
strategySelector.registerStrategyImplementor(
|
||||
EntityCopyObserver.class,
|
||||
EntityCopyAllowedLoggedObserver.SHORT_NAME,
|
||||
EntityCopyAllowedLoggedObserver.class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,14 +31,18 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.ObjectDeletedException;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.WrongClassException;
|
||||
import org.hibernate.boot.registry.selector.spi.StrategySelector;
|
||||
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.CascadePoint;
|
||||
import org.hibernate.engine.spi.CascadingAction;
|
||||
import org.hibernate.engine.spi.CascadingActions;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EntityCopyObserver;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.MergeEvent;
|
||||
import org.hibernate.event.spi.MergeEventListener;
|
||||
|
@ -47,6 +51,7 @@ import org.hibernate.internal.CoreMessageLogger;
|
|||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.type.ForeignKeyDirection;
|
||||
import org.hibernate.type.TypeHelper;
|
||||
|
||||
|
@ -59,15 +64,13 @@ import org.hibernate.type.TypeHelper;
|
|||
public class DefaultMergeEventListener extends AbstractSaveEventListener implements MergeEventListener {
|
||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( DefaultMergeEventListener.class );
|
||||
|
||||
private String entityCopyObserverStrategy;
|
||||
|
||||
@Override
|
||||
protected Map getMergeMap(Object anything) {
|
||||
return ( (MergeContext) anything ).invertMap();
|
||||
}
|
||||
|
||||
protected EntityCopyObserver createEntityCopyObserver() {
|
||||
return new EntityCopyNotAllowedObserver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given merge event.
|
||||
*
|
||||
|
@ -76,7 +79,7 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
|||
* @throws HibernateException
|
||||
*/
|
||||
public void onMerge(MergeEvent event) throws HibernateException {
|
||||
final EntityCopyObserver entityCopyObserver = createEntityCopyObserver();
|
||||
final EntityCopyObserver entityCopyObserver = createEntityCopyObserver( event.getSession().getFactory() );
|
||||
final MergeContext mergeContext = new MergeContext( event.getSession(), entityCopyObserver );
|
||||
try {
|
||||
onMerge( event, mergeContext );
|
||||
|
@ -88,6 +91,27 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
|||
}
|
||||
}
|
||||
|
||||
private EntityCopyObserver createEntityCopyObserver(SessionFactoryImplementor sessionFactory) {
|
||||
final ServiceRegistry serviceRegistry = sessionFactory.getServiceRegistry();
|
||||
if ( entityCopyObserverStrategy == null ) {
|
||||
final ConfigurationService configurationService
|
||||
= serviceRegistry.getService( ConfigurationService.class );
|
||||
entityCopyObserverStrategy = configurationService.getSetting(
|
||||
"hibernate.event.merge.entity_copy_observer",
|
||||
new ConfigurationService.Converter<String>() {
|
||||
@Override
|
||||
public String convert(Object value) {
|
||||
return value.toString();
|
||||
}
|
||||
},
|
||||
EntityCopyNotAllowedObserver.SHORT_NAME
|
||||
);
|
||||
LOG.debugf( "EntityCopyObserver strategy: %s", entityCopyObserverStrategy );
|
||||
}
|
||||
final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class );
|
||||
return strategySelector.resolveStrategy( EntityCopyObserver.class, entityCopyObserverStrategy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given merge event.
|
||||
*
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.hibernate.internal.util.collections.IdentitySet;
|
|||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
/**
|
||||
* An {@link EntityCopyObserver} implementation that allows multiple representations of
|
||||
* An {@link org.hibernate.event.spi.EntityCopyObserver} implementation that allows multiple representations of
|
||||
* the same persistent entity to be merged and provides logging of the entity copies that
|
||||
* that are detected.
|
||||
*
|
||||
|
@ -44,6 +44,8 @@ import org.hibernate.pretty.MessageHelper;
|
|||
public class EntityCopyAllowedLoggedObserver extends EntityCopyAllowedObserver {
|
||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EntityCopyAllowedLoggedObserver.class );
|
||||
|
||||
public static final String SHORT_NAME = "log";
|
||||
|
||||
// Tracks the number of entity copies per entity name.
|
||||
private Map<String, Integer> countsByEntityName;
|
||||
|
||||
|
|
|
@ -1,41 +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.event.internal;
|
||||
|
||||
/**
|
||||
* A {@link org.hibernate.event.spi.MergeEventListener} that allows merging
|
||||
* multiple representations of the same persistent entity.
|
||||
*
|
||||
* @author Gail Badner
|
||||
*/
|
||||
public class EntityCopyAllowedMergeEventListener extends DefaultMergeEventListener {
|
||||
|
||||
@Override
|
||||
protected EntityCopyObserver createEntityCopyObserver() {
|
||||
return EntityCopyAllowedLoggedObserver.isDebugLoggingEnabled() ?
|
||||
new EntityCopyAllowedLoggedObserver() :
|
||||
new EntityCopyAllowedObserver();
|
||||
}
|
||||
}
|
|
@ -23,23 +23,19 @@
|
|||
*/
|
||||
package org.hibernate.event.internal;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.event.spi.EntityCopyObserver;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.collections.IdentitySet;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
/**
|
||||
* An {@link EntityCopyObserver} implementation that allows multiple representations of
|
||||
* An {@link org.hibernate.event.spi.EntityCopyObserver} implementation that allows multiple representations of
|
||||
* the same persistent entity to be merged.
|
||||
*
|
||||
* @author Gail Badner
|
||||
*/
|
||||
public class EntityCopyAllowedObserver implements EntityCopyObserver {
|
||||
|
||||
public static final String SHORT_NAME = "allow";
|
||||
|
||||
@Override
|
||||
public void entityCopyDetected(
|
||||
Object managedEntity,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
package org.hibernate.event.internal;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.event.spi.EntityCopyObserver;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
|
@ -32,6 +33,8 @@ import org.hibernate.pretty.MessageHelper;
|
|||
*/
|
||||
public class EntityCopyNotAllowedObserver implements EntityCopyObserver {
|
||||
|
||||
public static final String SHORT_NAME = "disallow";
|
||||
|
||||
@Override
|
||||
public void entityCopyDetected(
|
||||
Object managedEntity,
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Set;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.event.spi.EntityCopyObserver;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
|
@ -53,7 +54,7 @@ import org.hibernate.pretty.MessageHelper;
|
|||
* MergeContext already contains an entry with a different entity as the key, but
|
||||
* with the same (managedEntity) value, this means that multiple entity representations
|
||||
* for the same persistent entity are being merged. If this happens,
|
||||
* {@link org.hibernate.event.internal.EntityCopyObserver#entityCopyDetected(
|
||||
* {@link org.hibernate.event.spi.EntityCopyObserver#entityCopyDetected(
|
||||
* Object managedEntity, Object mergeEntity1, Object mergeEntity2, org.hibernate.event.spi.EventSource)}
|
||||
* will be called. It is up to that method to determine the property course of
|
||||
* action for this situation.
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.event.internal;
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
|
|
@ -36,6 +36,7 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.event.spi.EntityCopyObserver;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
|
|
|
@ -29,9 +29,6 @@ import org.junit.Test;
|
|||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.event.internal.EntityCopyAllowedMergeEventListener;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
|
|
|
@ -30,11 +30,8 @@ import org.junit.Test;
|
|||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.event.internal.EntityCopyAllowedMergeEventListener;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.testing.FailureExpected;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -57,9 +54,12 @@ public class MergeMultipleEntityRepresentationsOrphanDeleteTest extends BaseCore
|
|||
};
|
||||
}
|
||||
|
||||
protected void afterSessionFactoryBuilt() {
|
||||
EventListenerRegistry registry = sessionFactory().getServiceRegistry().getService( EventListenerRegistry.class );
|
||||
registry.setListeners( EventType.MERGE, new EntityCopyAllowedMergeEventListener() );
|
||||
public void configure(Configuration cfg) {
|
||||
super.configure( cfg );
|
||||
cfg.setProperty(
|
||||
"hibernate.event.merge.entity_copy_observer",
|
||||
"allow"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -31,9 +31,7 @@ import org.hibernate.Hibernate;
|
|||
import org.hibernate.Session;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.event.internal.EntityCopyAllowedMergeEventListener;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.testing.FailureExpected;
|
||||
import org.hibernate.testing.FailureExpectedWithNewMetamodel;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
@ -58,9 +56,12 @@ public class MergeMultipleEntityRepresentationsTest extends BaseCoreFunctionalTe
|
|||
};
|
||||
}
|
||||
|
||||
protected void afterSessionFactoryBuilt() {
|
||||
EventListenerRegistry registry = sessionFactory().getServiceRegistry().getService( EventListenerRegistry.class );
|
||||
registry.setListeners( EventType.MERGE, new EntityCopyAllowedMergeEventListener() );
|
||||
public void configure(Configuration cfg) {
|
||||
super.configure( cfg );
|
||||
cfg.setProperty(
|
||||
"hibernate.event.merge.entity_copy_observer",
|
||||
"allow"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -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.jpa.event.internal.core;
|
||||
|
||||
import org.hibernate.event.internal.EntityCopyAllowedLoggedObserver;
|
||||
import org.hibernate.event.internal.EntityCopyAllowedObserver;
|
||||
import org.hibernate.event.internal.EntityCopyObserver;
|
||||
|
||||
/**
|
||||
* Overrides {@link JpaMergeEventListener} that allows merging multiple representations
|
||||
* of the same persistent entity.
|
||||
*
|
||||
* @author Gail Badner
|
||||
*/
|
||||
public class JpaEntityCopyAllowedMergeEventListener extends JpaMergeEventListener {
|
||||
|
||||
@Override
|
||||
protected EntityCopyObserver createEntityCopyObserver() {
|
||||
return EntityCopyAllowedLoggedObserver.isDebugLoggingEnabled() ?
|
||||
new EntityCopyAllowedLoggedObserver() :
|
||||
new EntityCopyAllowedObserver();
|
||||
}
|
||||
|
||||
}
|
|
@ -24,14 +24,11 @@
|
|||
package org.hibernate.jpa.test.emops;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
import org.hibernate.jpa.event.internal.core.JpaEntityCopyAllowedMergeEventListener;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
|
@ -41,21 +38,19 @@ 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.jpa.event.internal.core.JpaEntityCopyAllowedMergeEventListener}.
|
||||
* Tests merging multiple detached representations of the same entity.
|
||||
*
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-9106")
|
||||
public class MergeMultipleEntityRepresentationsAllowedTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected void afterEntityManagerFactoryBuilt() {
|
||||
super.afterEntityManagerFactoryBuilt();
|
||||
protected void addConfigOptions(Map options) {
|
||||
|
||||
SessionFactoryImplementor sfi = entityManagerFactory().unwrap( SessionFactoryImplementor.class );
|
||||
EventListenerRegistry registry = sfi.getServiceRegistry().getService( EventListenerRegistry.class );
|
||||
registry.setListeners( EventType.MERGE, new JpaEntityCopyAllowedMergeEventListener() );
|
||||
options.put(
|
||||
"hibernate.event.merge.entity_copy_observer",
|
||||
"allow"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue