HHH-7097 Cache naturalId mapping on load event

This commit is contained in:
Eric Dalquist 2012-02-21 11:15:21 -06:00
parent 13f4c830e1
commit c0b66d5298
4 changed files with 184 additions and 117 deletions

View File

@ -43,6 +43,7 @@ import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.engine.spi.PersistenceContext.CachedNaturalIdValueSource;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType;
@ -442,6 +443,22 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
event.getLockOptions(),
source
);
if (entity != null && persister.hasNaturalIdentifier()) {
final int[] naturalIdentifierProperties = persister.getNaturalIdentifierProperties();
final Object[] naturalId = new Object[naturalIdentifierProperties.length];
for ( int i = 0; i < naturalIdentifierProperties.length; i++ ) {
naturalId[i] = persister.getPropertyValue( entity, naturalIdentifierProperties[i] );
}
event.getSession().getPersistenceContext().cacheNaturalIdResolution(
persister,
event.getEntityId(),
naturalId,
CachedNaturalIdValueSource.LOAD
);
}
if ( event.isAssociationFetch() && source.getFactory().getStatistics().isStatisticsEnabled() ) {
source.getFactory().getStatisticsImplementor().fetchEntity( event.getEntityClassName() );

View File

@ -52,6 +52,13 @@ import static org.junit.Assert.assertTrue;
public class NaturalIdOnSingleManyToOneTest extends BaseCoreFunctionalTestCase {
private static final Logger log = Logger.getLogger( NaturalIdOnSingleManyToOneTest.class );
@Override
protected void cleanupTest() throws Exception {
this.cleanupCache();
this.deleteAllData();
}
@Test
public void testMappingProperties() {
log.warn("Commented out test");
@ -109,37 +116,17 @@ public class NaturalIdOnSingleManyToOneTest extends BaseCoreFunctionalTestCase {
// first query
List results = criteria.list();
assertEquals( 1, results.size() );
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
assertEquals(
"First query should be a miss", 1, stats
.getNaturalIdCacheMissCount()
);
assertEquals(
"Query result should be added to cache", 1, stats
.getNaturalIdCachePutCount()
);
assertEquals(
"Query count should be one", 1, stats
.getNaturalIdQueryExecutionCount()
);
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 2, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
// query a second time - result should be in session cache
criteria.list();
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
assertEquals(
"Second query should not be a miss", 1, stats
.getNaturalIdCacheMissCount()
);
assertEquals(
"Query count should be one", 1, stats
.getNaturalIdQueryExecutionCount()
);
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 2, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
// cleanup
tx.rollback();

View File

@ -48,6 +48,13 @@ import org.junit.Test;
*/
@SuppressWarnings("unchecked")
public class NaturalIdTest extends BaseCoreFunctionalTestCase {
@Override
protected void cleanupTest() throws Exception {
this.cleanupCache();
this.deleteAllData();
}
@Test
public void testMappingProperties() {
ClassMetadata metaData = sessionFactory().getClassMetadata(
@ -67,12 +74,12 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
Session s = openSession();
Transaction tx = s.beginTransaction();
State france = ( State ) s.load( State.class, 2 );
State france = this.getState( s, "Ile de France" );
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
s.getSessionFactory().getCache().evictNaturalIdRegions();
this.cleanupCache();
Statistics stats = sessionFactory().getStatistics();
stats.setStatisticsEnabled( true );
@ -85,37 +92,17 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
// first query
List results = criteria.list();
assertEquals( 1, results.size() );
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
assertEquals(
"First query should be a miss", 1, stats
.getNaturalIdCacheMissCount()
);
assertEquals(
"Query result should be added to cache", 1, stats
.getNaturalIdCachePutCount()
);
assertEquals(
"Query execution count should be one", 1, stats
.getNaturalIdQueryExecutionCount()
);
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 2, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
// query a second time - result should be cached in session
criteria.list();
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
assertEquals(
"Second query should not be a miss", 1, stats
.getNaturalIdCacheMissCount()
);
assertEquals(
"Query execution count should be one", 1, stats
.getNaturalIdQueryExecutionCount()
);
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 2, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
// cleanup
tx.rollback();
@ -128,35 +115,27 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
Session s = openSession();
Transaction tx = s.beginTransaction();
State france = ( State ) s.load( State.class, 2 );
State france = this.getState( s, "Ile de France" );
final NaturalIdLoadAccess naturalIdLoader = s.byNaturalId( Citizen.class );
naturalIdLoader.using( "ssn", "1234" ).using( "state", france );
//NaturalId cache gets populated during entity loading, need to clear it out
sessionFactory().getCache().evictNaturalIdRegions();
this.cleanupCache();
Statistics stats = sessionFactory().getStatistics();
stats.setStatisticsEnabled( true );
stats.clear();
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 0, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
// first query
Citizen citizen = (Citizen)naturalIdLoader.load();
assertNotNull( citizen );
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
assertEquals(
"First load should be a miss", 1, stats
.getNaturalIdCacheMissCount()
);
assertEquals(
"Query result should be added to cache", 1, stats
.getNaturalIdCachePutCount()
);
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 2, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
// cleanup
tx.rollback();
@ -165,51 +144,88 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
@Test
public void testNaturalIdLoaderCached() {
Statistics stats = sessionFactory().getStatistics();
stats.setStatisticsEnabled( true );
stats.clear();
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 0, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
saveSomeCitizens();
Statistics stats = sessionFactory().getStatistics();
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
assertEquals(
"First load should be a miss", 1, stats
.getNaturalIdCacheMissCount()
);
assertEquals(
"Query result should be added to cache", 3, stats
.getNaturalIdCachePutCount()
);
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 2, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
//Try NaturalIdLoadAccess after insert
Session s = openSession();
Transaction tx = s.beginTransaction();
State france = ( State ) s.load( State.class, 2 );
final NaturalIdLoadAccess naturalIdLoader = s.byNaturalId( Citizen.class );
State france = this.getState( s, "Ile de France" );
NaturalIdLoadAccess naturalIdLoader = s.byNaturalId( Citizen.class );
naturalIdLoader.using( "ssn", "1234" ).using( "state", france );
//Not clearing naturalId caches, should be warm from entity loading
stats.clear();
// first query
Citizen citizen = (Citizen)naturalIdLoader.load();
assertNotNull( citizen );
assertEquals( "NaturalId Cache Hits", 1, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
// cleanup
tx.rollback();
s.close();
//Try NaturalIdLoadAccess
s = openSession();
tx = s.beginTransaction();
this.cleanupCache();
stats.setStatisticsEnabled( true );
stats.clear();
// first query
citizen = (Citizen) s.get( Citizen.class, citizen.getId() );
assertNotNull( citizen );
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
// cleanup
tx.rollback();
s.close();
//Try NaturalIdLoadAccess after load
s = openSession();
tx = s.beginTransaction();
france = this.getState( s, "Ile de France" );
naturalIdLoader = s.byNaturalId( Citizen.class );
naturalIdLoader.using( "ssn", "1234" ).using( "state", france );
//Not clearing naturalId caches, should be warm from entity loading
stats.setStatisticsEnabled( true );
stats.clear();
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
// first query
Citizen citizen = (Citizen)naturalIdLoader.load();
citizen = (Citizen)naturalIdLoader.load();
assertNotNull( citizen );
assertEquals(
"Cache hits should be empty", 1, stats
.getNaturalIdCacheHitCount()
);
assertEquals(
"First load should be a miss", 0, stats
.getNaturalIdCacheMissCount()
);
assertEquals(
"Query result should be added to cache", 0, stats
.getNaturalIdCachePutCount()
);
assertEquals( "NaturalId Cache Hits", 1, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
// cleanup
tx.rollback();
@ -222,7 +238,7 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
Session s = openSession();
Transaction tx = s.beginTransaction();
State france = ( State ) s.load( State.class, 2 );
State france = this.getState( s, "Ile de France" );
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add(
Restrictions.naturalId().set( "ssn", "1234" ).set(
@ -232,7 +248,7 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
);
criteria.setCacheable( false );
s.getSessionFactory().getCache().evictNaturalIdRegions();
this.cleanupCache();
Statistics stats = sessionFactory().getStatistics();
stats.setStatisticsEnabled( true );
@ -310,6 +326,13 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
s.close();
}
private State getState(Session s, String name) {
Criteria criteria = s.createCriteria( State.class );
criteria.add( Restrictions.eq( "name", name ) );
criteria.setCacheable( true );
return (State) criteria.list().get( 0 );
}
@Override
protected void configure(Configuration cfg) {
cfg.setProperty( "hibernate.cache.use_query_cache", "true" );

View File

@ -23,20 +23,27 @@
*/
package org.hibernate.testing.junit4;
import static org.junit.Assert.fail;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
@ -44,6 +51,7 @@ import org.hibernate.cfg.Mappings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.AbstractReturningWork;
import org.hibernate.jdbc.Work;
@ -51,6 +59,7 @@ import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metamodel.MetadataSources;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.BootstrapServiceRegistry;
@ -58,20 +67,15 @@ import org.hibernate.service.BootstrapServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.hibernate.service.config.spi.ConfigurationService;
import org.hibernate.service.internal.BootstrapServiceRegistryImpl;
import org.hibernate.service.internal.StandardServiceRegistryImpl;
import org.junit.After;
import org.junit.Before;
import org.hibernate.testing.AfterClassOnce;
import org.hibernate.testing.BeforeClassOnce;
import org.hibernate.testing.OnExpectedFailure;
import org.hibernate.testing.OnFailure;
import org.hibernate.testing.SkipLog;
import org.hibernate.testing.cache.CachingRegionFactory;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.Before;
/**
* Applies functional testing logic for core Hibernate testing on top of {@link BaseUnitTestCase}
@ -418,6 +422,41 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase {
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() {
if ( sessionFactory != null ) {
@ -425,6 +464,7 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase {
sessionFactory.getCache().evictDefaultQueryRegion();
sessionFactory.getCache().evictEntityRegions();
sessionFactory.getCache().evictQueryRegions();
sessionFactory.getCache().evictNaturalIdRegions();
}
}