diff --git a/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdNonStrictReadWriteTest.java b/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdNonStrictReadWriteTest.java new file mode 100644 index 0000000000..722e29ba4c --- /dev/null +++ b/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdNonStrictReadWriteTest.java @@ -0,0 +1,14 @@ +package org.hibernate.test.naturalid.mutable.cached; + +import org.hibernate.cfg.Configuration; +import org.hibernate.testing.cache.CachingRegionFactory; + +public class CachedMutableNaturalIdNonStrictReadWriteTest extends + CachedMutableNaturalIdTest { + + @Override + public void configure(Configuration cfg) { + super.configure(cfg); + cfg.setProperty( CachingRegionFactory.DEFAULT_ACCESSTYPE, "nonstrict-read-write" ); + } +} diff --git a/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdStrictReadWriteTest.java b/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdStrictReadWriteTest.java new file mode 100644 index 0000000000..82e7c49c24 --- /dev/null +++ b/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdStrictReadWriteTest.java @@ -0,0 +1,154 @@ +package org.hibernate.test.naturalid.mutable.cached; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.cache.CachingRegionFactory; +import org.junit.Test; + +public class CachedMutableNaturalIdStrictReadWriteTest extends + CachedMutableNaturalIdTest { + + @Override + public void configure(Configuration cfg) { + super.configure(cfg); + cfg.setProperty( CachingRegionFactory.DEFAULT_ACCESSTYPE, "read-write" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-7278" ) + public void testInsertedNaturalIdCachedAfterTransactionSuccess() { + + Session session = openSession(); + session.getSessionFactory().getStatistics().clear(); + session.beginTransaction(); + Another it = new Another( "it"); + session.save( it ); + session.flush(); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("it"); + assertNotNull(it); + session.delete(it); + session.getTransaction().commit(); + assertEquals(1, session.getSessionFactory().getStatistics().getNaturalIdCacheHitCount()); + } + + @Test + @TestForIssue( jiraKey = "HHH-7278" ) + public void testInsertedNaturalIdNotCachedAfterTransactionFailure() { + + Session session = openSession(); + session.getSessionFactory().getStatistics().clear(); + session.beginTransaction(); + Another it = new Another( "it"); + session.save( it ); + session.flush(); + session.getTransaction().rollback(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("it"); + assertNull(it); + assertEquals(0, session.getSessionFactory().getStatistics().getNaturalIdCacheHitCount()); + } + + @Test + @TestForIssue( jiraKey = "HHH-7278" ) + public void testChangedNaturalIdCachedAfterTransactionSuccess() { + Session session = openSession(); + session.beginTransaction(); + Another it = new Another( "it"); + session.save( it ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("it"); + assertNotNull(it); + + it.setName("modified"); + session.flush(); + session.getTransaction().commit(); + session.close(); + + session.getSessionFactory().getStatistics().clear(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("modified"); + assertNotNull(it); + session.delete(it); + session.getTransaction().commit(); + session.close(); + + assertEquals(1, session.getSessionFactory().getStatistics().getNaturalIdCacheHitCount()); + } + + @Test + @TestForIssue( jiraKey = "HHH-7278" ) + public void testChangedNaturalIdNotCachedAfterTransactionFailure() { + Session session = openSession(); + session.beginTransaction(); + Another it = new Another( "it"); + session.save( it ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("it"); + assertNotNull(it); + + it.setName("modified"); + session.flush(); + session.getTransaction().rollback(); + session.close(); + + session.getSessionFactory().getStatistics().clear(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("modified"); + assertNull(it); + it = (Another) session.bySimpleNaturalId(Another.class).load("it"); + session.delete(it); + session.getTransaction().commit(); + session.close(); + + assertEquals(0, session.getSessionFactory().getStatistics().getNaturalIdCacheHitCount()); + } + + @Test + @TestForIssue( jiraKey = "HHH-7309" ) + public void testInsertUpdateEntity_NaturalIdCachedAfterTransactionSuccess() { + + Session session = openSession(); + session.getSessionFactory().getStatistics().clear(); + session.beginTransaction(); + Another it = new Another( "it"); + session.save( it ); // schedules an InsertAction + it.setSurname("1234"); // schedules an UpdateAction, without bug-fix + // this will re-cache natural-id with identical key and at same time invalidate it + session.flush(); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("it"); + assertNotNull(it); + session.delete(it); + session.getTransaction().commit(); + assertEquals("In a strict access strategy we would excpect a hit here", 1, session.getSessionFactory().getStatistics().getNaturalIdCacheHitCount()); + } +} diff --git a/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdTest.java b/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdTest.java new file mode 100755 index 0000000000..44390a3581 --- /dev/null +++ b/hibernate-core/src/matrix/java/org/hibernate/test/naturalid/mutable/cached/CachedMutableNaturalIdTest.java @@ -0,0 +1,185 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.test.naturalid.mutable.cached; + +import java.io.Serializable; + +import org.junit.Test; + +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Tests of mutable natural ids stored in second level cache + * + * @author Guenther Demetz + * @author Steve Ebersole + */ +public abstract class CachedMutableNaturalIdTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] {Another.class, AllCached.class}; + } + + @Override + public void configure(Configuration cfg) { + cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" ); + cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); + } + + @Test + public void testNaturalIdChangedWhileAttached() { + Session session = openSession(); + session.beginTransaction(); + Another it = new Another( "it" ); + session.save( it ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId( Another.class ).load( "it" ); + assertNotNull( it ); + // change it's name + it.setName( "it2" ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId( Another.class ).load( "it" ); + assertNull( it ); + it = (Another) session.bySimpleNaturalId( Another.class ).load( "it2" ); + assertNotNull( it ); + session.delete( it ); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testNaturalIdChangedWhileDetached() { + Session session = openSession(); + session.beginTransaction(); + Another it = new Another( "it" ); + session.save( it ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId( Another.class ).load( "it" ); + assertNotNull( it ); + session.getTransaction().commit(); + session.close(); + + it.setName( "it2" ); + + session = openSession(); + session.beginTransaction(); + session.update( it ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId( Another.class ).load( "it" ); + assertNull( it ); + it = (Another) session.bySimpleNaturalId( Another.class ).load( "it2" ); + assertNotNull( it ); + session.delete( it ); + session.getTransaction().commit(); + session.close(); + } + + + + @Test + public void testNaturalIdRecachingWhenNeeded() { + Session session = openSession(); + session.getSessionFactory().getStatistics().clear(); + session.beginTransaction(); + Another it = new Another( "it"); + session.save( it ); + Serializable id = it.getId(); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + for (int i=0; i < 10; i++) { + session.beginTransaction(); + it = (Another) session.byId(Another.class).load(id); + if (i == 9) { + it.setName("name" + i); + } + it.setSurname("surname" + i); // changing something but not the natural-id's + session.getTransaction().commit(); + } + + session = openSession(); + session.beginTransaction(); + it = (Another) session.bySimpleNaturalId(Another.class).load("it"); + assertNull(it); + assertEquals(0, session.getSessionFactory().getStatistics().getNaturalIdCacheHitCount()); + it = (Another) session.byId(Another.class).load(id); + session.delete(it); + session.getTransaction().commit(); + + // finally there should be only 2 NaturalIdCache puts : 1. insertion, 2. when updating natural-id from 'it' to 'name9' + assertEquals(2, session.getSessionFactory().getStatistics().getNaturalIdCachePutCount()); + } + + @Test + @TestForIssue( jiraKey = "HHH-7245" ) + public void testNaturalIdChangeAfterResolveEntityFrom2LCache() { + Session session = openSession(); + session.beginTransaction(); + AllCached it = new AllCached( "it" ); + + session.save( it ); + Serializable id = it.getId(); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + it = (AllCached) session.byId( AllCached.class ).load( id ); + + it.setName( "it2" ); + it = (AllCached) session.bySimpleNaturalId( AllCached.class ).load( "it" ); + assertNull( it ); + it = (AllCached) session.bySimpleNaturalId( AllCached.class ).load( "it2" ); + assertNotNull( it ); + session.delete( it ); + session.getTransaction().commit(); + session.close(); + } +} + diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cache/CachingRegionFactory.java b/hibernate-testing/src/main/java/org/hibernate/testing/cache/CachingRegionFactory.java index 3bee9ad1a4..5f5634718e 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/cache/CachingRegionFactory.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cache/CachingRegionFactory.java @@ -46,7 +46,9 @@ public class CachingRegionFactory implements RegionFactory { private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, CachingRegionFactory.class.getName() ); + public static String DEFAULT_ACCESSTYPE = "DefaultAccessType"; private Settings settings; + private Properties properties; public CachingRegionFactory() { LOG.warn( "CachingRegionFactory should be only used for testing." ); } @@ -54,11 +56,13 @@ public class CachingRegionFactory implements RegionFactory { public CachingRegionFactory(Properties properties) { //add here to avoid run into catch LOG.warn( "CachingRegionFactory should be only used for testing." ); + this.properties=properties; } @Override public void start(Settings settings, Properties properties) throws CacheException { this.settings=settings; + this.properties=properties; } @Override @@ -72,7 +76,10 @@ public class CachingRegionFactory implements RegionFactory { @Override public AccessType getDefaultAccessType() { - return AccessType.NONSTRICT_READ_WRITE; + if (properties != null && properties.get(DEFAULT_ACCESSTYPE) != null) { + return AccessType.fromExternalName(properties.getProperty(DEFAULT_ACCESSTYPE)); + } + return AccessType.READ_WRITE; } @Override diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cache/NonstrictReadWriteNaturalIdRegionAccessStrategy.java b/hibernate-testing/src/main/java/org/hibernate/testing/cache/NonstrictReadWriteNaturalIdRegionAccessStrategy.java index 381971908c..3de694f7d6 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/cache/NonstrictReadWriteNaturalIdRegionAccessStrategy.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cache/NonstrictReadWriteNaturalIdRegionAccessStrategy.java @@ -42,4 +42,31 @@ class NonstrictReadWriteNaturalIdRegionAccessStrategy extends BaseNaturalIdRegio public void remove(Object key) throws CacheException { evict( key ); } + + /** + * Returns false since this is an asynchronous cache access strategy. + * @see org.hibernate.cache.ehcache.internal.strategy.NonStrictReadWriteEhcacheNaturalIdRegionAccessStrategy + */ + @Override + public boolean insert(Object key, Object value ) throws CacheException { + return false; + } + + /** + * Returns false since this is a non-strict read/write cache access strategy + * @see org.hibernate.cache.ehcache.internal.strategy.NonStrictReadWriteEhcacheNaturalIdRegionAccessStrategy + */ + @Override + public boolean afterInsert(Object key, Object value ) throws CacheException { + return false; + } + + /** + * Removes the entry since this is a non-strict read/write cache strategy. + * @see org.hibernate.cache.ehcache.internal.strategy.NonStrictReadWriteEhcacheNaturalIdRegionAccessStrategy + */ + public boolean update(Object key, Object value ) throws CacheException { + remove( key ); + return false; + } }