diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index 6683a3411527..e66f904bf29a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -43,6 +43,7 @@ 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 @@ protected Object loadFromDatasource( 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() ); diff --git a/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdOnSingleManyToOneTest.java b/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdOnSingleManyToOneTest.java index 8435a46f4c39..aedf22bcbe85 100644 --- a/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdOnSingleManyToOneTest.java +++ b/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdOnSingleManyToOneTest.java @@ -52,6 +52,13 @@ 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 void testManyToOneNaturalIdCached() { // 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(); diff --git a/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdTest.java b/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdTest.java index 2fff3bdd9a0f..f4e79da47fdb 100644 --- a/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdTest.java +++ b/hibernate-core/src/matrix/java/org/hibernate/test/annotations/naturalid/NaturalIdTest.java @@ -48,6 +48,13 @@ */ @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 void testNaturalIdCached() { 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 void testNaturalIdCached() { // 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 void testNaturalIdLoaderNotCached() { 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 void testNaturalIdLoaderNotCached() { @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.setStatisticsEnabled( true ); stats.clear(); - assertEquals( - "Cache hits should be empty", 0, stats - .getNaturalIdCacheHitCount() - ); // first query Citizen 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(); + 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(); + + // first query + 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(); @@ -222,7 +238,7 @@ public void testNaturalIdUncached() { 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 void testNaturalIdUncached() { ); criteria.setCacheable( false ); - s.getSessionFactory().getCache().evictNaturalIdRegions(); + this.cleanupCache(); Statistics stats = sessionFactory().getStatistics(); stats.setStatisticsEnabled( true ); @@ -310,6 +326,13 @@ private void saveSomeCitizens() { 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" ); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java index 398785f1e0ee..93c6115e988d 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java @@ -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.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.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.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 final void afterTest() throws Exception { assertAllDataRemoved(); } + + protected void deleteAllData() { + // Get all the entities the session factory knows about + final Map allClassMetadata = this.sessionFactory().getAllClassMetadata(); + Set entityTypes = new LinkedHashSet(allClassMetadata.values()); + + do { + final Set failedEntitieTypes = new HashSet(); + + 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 @@ protected void cleanupCache() { sessionFactory.getCache().evictDefaultQueryRegion(); sessionFactory.getCache().evictEntityRegions(); sessionFactory.getCache().evictQueryRegions(); + sessionFactory.getCache().evictNaturalIdRegions(); } }