HHH-7097 Cache naturalId mapping on load event
This commit is contained in:
parent
13f4c830e1
commit
c0b66d5298
|
@ -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() );
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue