HHH-7097 - Entity load event doesn't result in naturalId->pk caching

This commit is contained in:
Steve Ebersole 2012-03-01 13:22:09 -06:00
parent 7678917313
commit a889b6cca1
6 changed files with 75 additions and 65 deletions

View File

@ -39,6 +39,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type;
/** /**
* Maintains a {@link org.hibernate.engine.spi.PersistenceContext}-level 2-way cross-reference (xref) between the * Maintains a {@link org.hibernate.engine.spi.PersistenceContext}-level 2-way cross-reference (xref) between the
@ -74,11 +75,12 @@ public class NaturalIdXrefDelegate {
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache ); naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
} }
final CachedNaturalId cachedNaturalId = new CachedNaturalId( persister, naturalIdValues ); final boolean justAddedToLocalCache = entityNaturalIdResolutionCache.cache( pk, naturalIdValues );
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, cachedNaturalId );
entityNaturalIdResolutionCache.naturalIdToPkMap.put( cachedNaturalId, pk );
//If second-level caching is enabled cache the resolution there as well // If second-level caching is enabled cache the resolution there as well
// NOTE : the checks using 'justAddedToLocalCache' below protect only the stat journaling, not actually
// putting into the shared cache. we still put into the shared cache because that might have locking
// semantics that we need to honor.
if ( persister.hasNaturalIdCache() ) { if ( persister.hasNaturalIdCache() ) {
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy(); final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session() ); final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session() );
@ -94,7 +96,7 @@ public class NaturalIdXrefDelegate {
null null
); );
if ( put && factory.getStatistics().isStatisticsEnabled() ) { if ( put && justAddedToLocalCache && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatisticsImplementor() factory.getStatisticsImplementor()
.naturalIdCachePut( naturalIdCacheAccessStrategy.getRegion().getName() ); .naturalIdCachePut( naturalIdCacheAccessStrategy.getRegion().getName() );
} }
@ -110,7 +112,7 @@ public class NaturalIdXrefDelegate {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) { public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
final boolean put = naturalIdCacheAccessStrategy.afterInsert( naturalIdCacheKey, pk ); final boolean put = naturalIdCacheAccessStrategy.afterInsert( naturalIdCacheKey, pk );
if ( put && factory.getStatistics().isStatisticsEnabled() ) { if ( put && justAddedToLocalCache && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatisticsImplementor().naturalIdCachePut( factory.getStatisticsImplementor().naturalIdCachePut(
naturalIdCacheAccessStrategy.getRegion().getName() ); naturalIdCacheAccessStrategy.getRegion().getName() );
} }
@ -130,7 +132,7 @@ public class NaturalIdXrefDelegate {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) { public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
final boolean put = naturalIdCacheAccessStrategy.afterUpdate( naturalIdCacheKey, pk, lock ); final boolean put = naturalIdCacheAccessStrategy.afterUpdate( naturalIdCacheKey, pk, lock );
if ( put && factory.getStatistics().isStatisticsEnabled() ) { if ( put && justAddedToLocalCache && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatisticsImplementor().naturalIdCachePut( factory.getStatisticsImplementor().naturalIdCachePut(
naturalIdCacheAccessStrategy.getRegion().getName() ); naturalIdCacheAccessStrategy.getRegion().getName() );
} }
@ -310,16 +312,49 @@ public class NaturalIdXrefDelegate {
private static class NaturalIdResolutionCache implements Serializable { private static class NaturalIdResolutionCache implements Serializable {
private final EntityPersister persister; private final EntityPersister persister;
private final Type[] naturalIdTypes;
private Map<Serializable, CachedNaturalId> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, CachedNaturalId>();
private Map<CachedNaturalId, Serializable> naturalIdToPkMap = new ConcurrentHashMap<CachedNaturalId, Serializable>();
private NaturalIdResolutionCache(EntityPersister persister) { private NaturalIdResolutionCache(EntityPersister persister) {
this.persister = persister; this.persister = persister;
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
naturalIdTypes = new Type[ naturalIdPropertyIndexes.length ];
int i = 0;
for ( int naturalIdPropertyIndex : naturalIdPropertyIndexes ) {
naturalIdTypes[i++] = persister.getPropertyType( persister.getPropertyNames()[ naturalIdPropertyIndex ] );
}
} }
public EntityPersister getPersister() { public EntityPersister getPersister() {
return persister; return persister;
} }
private Map<Serializable, CachedNaturalId> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, CachedNaturalId>(); public boolean cache(Serializable pk, Object[] naturalIdValues) {
private Map<CachedNaturalId, Serializable> naturalIdToPkMap = new ConcurrentHashMap<CachedNaturalId, Serializable>(); final CachedNaturalId initial = pkToNaturalIdMap.get( pk );
if ( initial != null ) {
if ( areSame( naturalIdValues, initial.getValues() ) ) {
return false;
}
}
final CachedNaturalId cachedNaturalId = new CachedNaturalId( persister, naturalIdValues );
pkToNaturalIdMap.put( pk, cachedNaturalId );
naturalIdToPkMap.put( cachedNaturalId, pk );
return true;
}
private boolean areSame(Object[] naturalIdValues, Object[] values) {
// lengths have already been verified at this point
for ( int i = 0; i < naturalIdTypes.length; i++ ) {
if ( naturalIdTypes[i].compare( naturalIdValues[i], values[i] ) != 0 ) {
return false;
}
}
return true;
}
} }
} }

View File

@ -37,13 +37,13 @@ import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.internal.TwoPhaseLoad; import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status; import org.hibernate.engine.spi.Status;
import org.hibernate.engine.spi.PersistenceContext.CachedNaturalIdValueSource;
import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType; import org.hibernate.event.spi.EventType;

View File

@ -26,6 +26,8 @@ package org.hibernate.test.annotations.naturalid;
import java.util.List; import java.util.List;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.hibernate.Criteria; import org.hibernate.Criteria;
@ -52,11 +54,16 @@ import static org.junit.Assert.assertTrue;
public class NaturalIdOnSingleManyToOneTest extends BaseCoreFunctionalTestCase { public class NaturalIdOnSingleManyToOneTest extends BaseCoreFunctionalTestCase {
private static final Logger log = Logger.getLogger( NaturalIdOnSingleManyToOneTest.class ); private static final Logger log = Logger.getLogger( NaturalIdOnSingleManyToOneTest.class );
@Override @After
protected void cleanupTest() throws Exception { public void cleanupData() {
this.cleanupCache(); super.cleanupCache();
Session s = sessionFactory().openSession();
this.deleteAllData(); s.beginTransaction();
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
s.createQuery( "delete Citizen" ).executeUpdate();
s.createQuery( "delete State" ).executeUpdate();
s.getTransaction().commit();
s.close();
} }
@Test @Test

View File

@ -38,6 +38,8 @@ import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.stat.Statistics; import org.hibernate.stat.Statistics;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
/** /**
@ -48,11 +50,16 @@ import org.junit.Test;
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class NaturalIdTest extends BaseCoreFunctionalTestCase { public class NaturalIdTest extends BaseCoreFunctionalTestCase {
@Override @After
protected void cleanupTest() throws Exception { public void cleanupData() {
this.cleanupCache(); super.cleanupCache();
Session s = sessionFactory().openSession();
this.deleteAllData(); s.beginTransaction();
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
s.createQuery( "delete Citizen" ).executeUpdate();
s.createQuery( "delete State" ).executeUpdate();
s.getTransaction().commit();
s.close();
} }
@Test @Test

View File

@ -133,9 +133,9 @@ public class ImmutableEntityNaturalIdTest extends BaseCoreFunctionalTestCase {
building = (Building) naturalIdLoader.load(); building = (Building) naturalIdLoader.load();
assertNull( building ); assertNull( building );
assertEquals( "Cache hits should be one after second query", 1, stats.getNaturalIdCacheHitCount() ); assertEquals( "Cache hits should be one after second query", 1, stats.getNaturalIdCacheHitCount() );
assertEquals( "Cache misses should be one after second query", 1, stats.getNaturalIdCacheMissCount() ); assertEquals( "Cache misses should be two after second query", 2, stats.getNaturalIdCacheMissCount() );
assertEquals( "Cache put should be one after second query", 1, stats.getNaturalIdCachePutCount() ); assertEquals( "Cache put should be one after second query", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "Query count should be one after second query", 1, stats.getNaturalIdQueryExecutionCount() ); assertEquals( "Query count should be two after second query", 2, stats.getNaturalIdQueryExecutionCount() );
// cleanup // cleanup
tx.commit(); tx.commit();
@ -152,17 +152,13 @@ public class ImmutableEntityNaturalIdTest extends BaseCoreFunctionalTestCase {
building = (Building) naturalIdLoader.load(); building = (Building) naturalIdLoader.load();
assertNull( building ); assertNull( building );
assertEquals( "Cache hits should be one after third query", 1, stats.getNaturalIdCacheHitCount() ); assertEquals( "Cache hits should be one after third query", 1, stats.getNaturalIdCacheHitCount() );
assertEquals( "Cache misses should be one after third query", 2, stats.getNaturalIdCacheMissCount() ); assertEquals( "Cache misses should be one after third query", 3, stats.getNaturalIdCacheMissCount() );
assertEquals( "Cache put should be one after third query", 1, stats.getNaturalIdCachePutCount() ); assertEquals( "Cache put should be one after third query", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "Query count should be one after third query", 2, stats.getNaturalIdQueryExecutionCount() ); assertEquals( "Query count should be one after third query", 3, stats.getNaturalIdQueryExecutionCount() );
// cleanup // cleanup
tx.rollback(); tx.rollback();
s.close(); s.close();
} }
@Override @Override

View File

@ -423,41 +423,6 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase {
assertAllDataRemoved(); assertAllDataRemoved();
} }
protected void deleteAllData() {
// Get all the entities the session factory knows about
final Map<String, ClassMetadata> allClassMetadata = this.sessionFactory().getAllClassMetadata();
Set<ClassMetadata> entityTypes = new LinkedHashSet<ClassMetadata>(allClassMetadata.values());
do {
final Set<ClassMetadata> failedEntitieTypes = new HashSet<ClassMetadata>();
for (final ClassMetadata entityType : entityTypes) {
final String entityClassName = entityType.getEntityName();
final Session s = openSession();
final Transaction tx = s.beginTransaction();
try {
final Criteria criteria = s.createCriteria( entityClassName );
final List<?> entities = criteria.list();
for (final Object entity : entities) {
s.delete( entity);
}
tx.commit();
}
catch (ConstraintViolationException e) {
failedEntitieTypes.add(entityType);
tx.rollback();
}
finally {
s.close();
}
}
entityTypes = failedEntitieTypes;
} while (!entityTypes.isEmpty());
}
protected void cleanupCache() { protected void cleanupCache() {
if ( sessionFactory != null ) { if ( sessionFactory != null ) {
sessionFactory.getCache().evictCollectionRegions(); sessionFactory.getCache().evictCollectionRegions();