HHH-7179 Complete support for Natural Id caching for Infinispan
This commit is contained in:
parent
2afa747ef9
commit
5fa28e87ea
|
@ -28,6 +28,7 @@ import java.util.Arrays;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.hibernate.cache.spi.CacheDataDescription;
|
|||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.infinispan.collection.CollectionRegionImpl;
|
||||
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
|
||||
import org.hibernate.cache.infinispan.impl.BaseRegion;
|
||||
import org.hibernate.cache.infinispan.impl.ClassLoaderAwareCache;
|
||||
import org.hibernate.cache.infinispan.query.QueryResultsRegionImpl;
|
||||
import org.hibernate.cache.infinispan.timestamp.TimestampTypeOverrides;
|
||||
|
@ -37,8 +36,6 @@ import org.hibernate.cache.infinispan.timestamp.TimestampsRegionImpl;
|
|||
import org.hibernate.cache.infinispan.tm.HibernateTransactionManagerLookup;
|
||||
import org.hibernate.cache.infinispan.util.CacheAdapter;
|
||||
import org.hibernate.cache.infinispan.util.CacheAdapterImpl;
|
||||
import org.hibernate.cache.infinispan.util.CacheCommandFactory;
|
||||
import org.hibernate.cache.spi.CacheDataDescription;
|
||||
import org.hibernate.cache.spi.CollectionRegion;
|
||||
import org.hibernate.cache.spi.EntityRegion;
|
||||
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||
|
@ -323,7 +320,7 @@ public class InfinispanRegionFactory implements RegionFactory {
|
|||
}
|
||||
}
|
||||
|
||||
protected HibernateTransactionManagerLookup createTransactionManagerLookup(
|
||||
protected org.infinispan.transaction.lookup.TransactionManagerLookup createTransactionManagerLookup(
|
||||
Settings settings, Properties properties) {
|
||||
return new HibernateTransactionManagerLookup(settings, properties);
|
||||
}
|
||||
|
@ -332,13 +329,19 @@ public class InfinispanRegionFactory implements RegionFactory {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
public void stop() {
|
||||
log.debug("Clear region references and stop Infinispan cache manager");
|
||||
getCacheCommandFactory(manager.getCache()).clearRegions(regionNames);
|
||||
regionNames.clear();
|
||||
log.debug("Stop region factory");
|
||||
stopCacheRegions();
|
||||
stopCacheManager();
|
||||
}
|
||||
|
||||
protected void stopCacheRegions() {
|
||||
log.debug("Clear region references");
|
||||
getCacheCommandFactory(manager.getCache()).clearRegions(regionNames);
|
||||
regionNames.clear();
|
||||
}
|
||||
|
||||
protected void stopCacheManager() {
|
||||
log.debug("Stop cache manager");
|
||||
manager.stop();
|
||||
}
|
||||
|
||||
|
@ -384,6 +387,9 @@ public class InfinispanRegionFactory implements RegionFactory {
|
|||
TypeOverrides collectionOverrides = new TypeOverrides();
|
||||
collectionOverrides.setCacheName(DEF_ENTITY_RESOURCE);
|
||||
typeOverrides.put(COLLECTION_KEY, collectionOverrides);
|
||||
TypeOverrides naturalIdOverrides = new TypeOverrides();
|
||||
naturalIdOverrides.setCacheName(DEF_ENTITY_RESOURCE);
|
||||
typeOverrides.put(NATURAL_ID_KEY, naturalIdOverrides);
|
||||
TypeOverrides timestampOverrides = new TimestampTypeOverrides();
|
||||
timestampOverrides.setCacheName(DEF_TIMESTAMPS_RESOURCE);
|
||||
typeOverrides.put(TIMESTAMPS_KEY, timestampOverrides);
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.hibernate.cache.spi.access.SoftLock;
|
|||
*/
|
||||
public class TransactionalAccessDelegate {
|
||||
private static final Log log = LogFactory.getLog(TransactionalAccessDelegate.class);
|
||||
private static final boolean isTrace = log.isTraceEnabled();
|
||||
protected final CacheAdapter cacheAdapter;
|
||||
protected final BaseRegion region;
|
||||
protected final PutFromLoadValidator putValidator;
|
||||
|
@ -68,11 +69,15 @@ public class TransactionalAccessDelegate {
|
|||
}
|
||||
|
||||
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
|
||||
if (!region.checkValid())
|
||||
if (!region.checkValid()) {
|
||||
if (isTrace) log.tracef("Region %s not valid", region.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!putValidator.acquirePutFromLoadLock(key))
|
||||
if (!putValidator.acquirePutFromLoadLock(key)) {
|
||||
if (isTrace) log.tracef("Put from load lock not acquired for key %s", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
cacheAdapter.putForExternalRead(key, value);
|
||||
|
|
|
@ -31,6 +31,7 @@ public class NaturalIdRegionImpl extends BaseTransactionalDataRegion implements
|
|||
}
|
||||
throw new CacheException("Unsupported access type [" + accessType.getExternalName() + "]");
|
||||
}
|
||||
|
||||
public PutFromLoadValidator getPutFromLoadValidator() {
|
||||
return new PutFromLoadValidator(transactionManager);
|
||||
}
|
||||
|
|
|
@ -19,24 +19,14 @@ class TransactionalAccess implements NaturalIdRegionAccessStrategy {
|
|||
this.delegate = new TransactionalAccessDelegate( region, region.getPutFromLoadValidator() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterInsert(Object key, Object value) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insert(Object key, Object value) throws CacheException {
|
||||
return false;
|
||||
return delegate.insert(key, value, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Object key, Object value) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
|
||||
return false;
|
||||
return delegate.update(key, value, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,7 +49,6 @@ class TransactionalAccess implements NaturalIdRegionAccessStrategy {
|
|||
return delegate.get( key, txTimestamp );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
|
||||
return delegate.putFromLoad( key, value, txTimestamp, version );
|
||||
|
@ -81,7 +70,6 @@ class TransactionalAccess implements NaturalIdRegionAccessStrategy {
|
|||
delegate.removeAll();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SoftLock lockItem(Object key, Object version) throws CacheException {
|
||||
return null;
|
||||
|
@ -100,5 +88,14 @@ class TransactionalAccess implements NaturalIdRegionAccessStrategy {
|
|||
public void unlockRegion(SoftLock lock) throws CacheException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterInsert(Object key, Object value) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ import java.util.Properties;
|
|||
import javax.transaction.Transaction;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.infinispan.notifications.Listener;
|
||||
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
||||
import org.hibernate.cache.infinispan.util.CacheAdapter;
|
||||
|
@ -18,7 +16,6 @@ import org.hibernate.cache.spi.RegionFactory;
|
|||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
@Listener
|
||||
public class QueryResultsRegionImpl extends BaseTransactionalDataRegion implements QueryResultsRegion {
|
||||
private boolean localOnly;
|
||||
|
||||
|
|
|
@ -536,7 +536,7 @@ public class InfinispanRegionFactoryTestCase {
|
|||
private InfinispanRegionFactory createRegionFactory(final EmbeddedCacheManager manager, Properties p) {
|
||||
final InfinispanRegionFactory factory = new InfinispanRegionFactory() {
|
||||
@Override
|
||||
protected HibernateTransactionManagerLookup createTransactionManagerLookup(Settings settings, Properties properties) {
|
||||
protected org.infinispan.transaction.lookup.TransactionManagerLookup createTransactionManagerLookup(Settings settings, Properties properties) {
|
||||
return new HibernateTransactionManagerLookup(null, null) {
|
||||
@Override
|
||||
public TransactionManager getTransactionManager() throws Exception {
|
||||
|
|
|
@ -24,10 +24,18 @@
|
|||
package org.hibernate.test.cache.infinispan.functional;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.NaturalIdLoadAccess;
|
||||
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.infinispan.util.logging.Log;
|
||||
import org.infinispan.util.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.Session;
|
||||
|
@ -37,6 +45,8 @@ import org.hibernate.cfg.Configuration;
|
|||
import org.hibernate.stat.SecondLevelCacheStatistics;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.infinispan.test.TestingUtil.withTx;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -54,89 +64,104 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
super.configure( cfg );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Citizen.class, State.class,
|
||||
NaturalIdOnManyToOne.class
|
||||
};
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupData() throws Exception {
|
||||
super.cleanupCache();
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = sessionFactory().openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
|
||||
s.createQuery( "delete Citizen" ).executeUpdate();
|
||||
s.createQuery( "delete State" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntityCache() throws Exception {
|
||||
Statistics stats = sessionFactory().getStatistics();
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
Item item = new Item( "chris", "Chris's Item" );
|
||||
beginTx();
|
||||
try {
|
||||
final Item item = new Item( "chris", "Chris's Item" );
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.persist( item );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
});
|
||||
|
||||
log.info( "Entry persisted, let's load and delete it." );
|
||||
log.info("Entry persisted, let's load and delete it.");
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
Item found = (Item) s.load( Item.class, item.getId() );
|
||||
log.info( stats.toString() );
|
||||
assertEquals( item.getDescription(), found.getDescription() );
|
||||
assertEquals( 0, stats.getSecondLevelCacheMissCount() );
|
||||
assertEquals( 1, stats.getSecondLevelCacheHitCount() );
|
||||
s.delete( found );
|
||||
Item found = (Item) s.load(Item.class, item.getId());
|
||||
log.info(stats.toString());
|
||||
assertEquals(item.getDescription(), found.getDescription());
|
||||
assertEquals(0, stats.getSecondLevelCacheMissCount());
|
||||
assertEquals(1, stats.getSecondLevelCacheHitCount());
|
||||
s.delete(found);
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionCache() throws Exception {
|
||||
Statistics stats = sessionFactory().getStatistics();
|
||||
final Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
|
||||
Item item = new Item( "chris", "Chris's Item" );
|
||||
Item another = new Item( "another", "Owned Item" );
|
||||
final Item item = new Item( "chris", "Chris's Item" );
|
||||
final Item another = new Item( "another", "Owned Item" );
|
||||
item.addItem( another );
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
s.persist( item );
|
||||
s.persist( another );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
});
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
Item loaded = (Item) s.load( Item.class, item.getId() );
|
||||
assertEquals( 1, loaded.getItems().size() );
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
});
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
|
||||
Item loadedWithCachedCollection = (Item) s.load( Item.class, item.getId() );
|
||||
|
@ -147,13 +172,9 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
Map cacheEntries = cStats.getEntries();
|
||||
assertEquals( 1, cacheEntries.size() );
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -164,6 +185,7 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
VersionedItem item = null;
|
||||
Transaction txn = null;
|
||||
Session s = null;
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
s = openSession();
|
||||
|
@ -191,12 +213,12 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
try {
|
||||
s = openSession();
|
||||
txn = s.beginTransaction();
|
||||
s.update( item );
|
||||
s.update(item);
|
||||
txn.commit();
|
||||
fail( "expected stale write to fail" );
|
||||
fail("expected stale write to fail");
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTxExpected( e );
|
||||
setRollbackOnlyTxExpected(e);
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
|
@ -215,7 +237,7 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
Object entry = slcs.getEntries().get( item.getId() );
|
||||
Long cachedVersionValue;
|
||||
cachedVersionValue = (Long) ((CacheEntry) entry).getVersion();
|
||||
assertEquals( initialVersion.longValue(), cachedVersionValue.longValue() );
|
||||
assertEquals(initialVersion.longValue(), cachedVersionValue.longValue());
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
|
@ -243,7 +265,7 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
SecondLevelCacheStatistics slcs = stats.getSecondLevelCacheStatistics( Item.class.getName() );
|
||||
sessionFactory().getCache().evictEntityRegion( Item.class.getName() );
|
||||
|
||||
assertEquals( 0, slcs.getPutCount() );
|
||||
assertEquals(0, slcs.getPutCount());
|
||||
assertEquals( 0, slcs.getElementCountInMemory() );
|
||||
assertEquals( 0, slcs.getEntries().size() );
|
||||
|
||||
|
@ -304,7 +326,7 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
// cleanup
|
||||
s = openSession();
|
||||
t = s.beginTransaction();
|
||||
s.delete( i );
|
||||
s.delete(i);
|
||||
t.commit();
|
||||
s.close();
|
||||
}
|
||||
|
@ -440,4 +462,197 @@ public class BasicTransactionalTestCase extends SingleNodeTestCase {
|
|||
assertEquals( 0, cacheEntries.size() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdCached() throws Exception {
|
||||
saveSomeCitizens();
|
||||
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
State france = BasicTransactionalTestCase.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 );
|
||||
|
||||
BasicTransactionalTestCase.this.cleanupCache();
|
||||
|
||||
Statistics stats = sessionFactory().getStatistics();
|
||||
stats.setStatisticsEnabled( true );
|
||||
stats.clear();
|
||||
assertEquals(
|
||||
"Cache hits should be empty", 0, stats
|
||||
.getNaturalIdCacheHitCount()
|
||||
);
|
||||
|
||||
// first query
|
||||
List results = criteria.list();
|
||||
assertEquals( 1, results.size() );
|
||||
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
|
||||
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
|
||||
|
||||
// query a second time - result should be cached in session
|
||||
criteria.list();
|
||||
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
|
||||
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
|
||||
|
||||
// cleanup
|
||||
tx.rollback();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdLoaderCached() throws Exception {
|
||||
final 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();
|
||||
|
||||
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
|
||||
final Citizen citizen = withTx(tm, new Callable<Citizen>() {
|
||||
@Override
|
||||
public Citizen call() throws Exception {
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
State france = BasicTransactionalTestCase.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", 0, stats.getNaturalIdCachePutCount());
|
||||
assertEquals("NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount());
|
||||
|
||||
// cleanup
|
||||
tx.rollback();
|
||||
s.close();
|
||||
return citizen;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Clear caches manually via cache manager (it's faster!!)
|
||||
this.cleanupCache();
|
||||
Thread.sleep(PutFromLoadValidator.NAKED_PUT_INVALIDATION_PERIOD + TimeUnit.SECONDS.toMillis(1));
|
||||
stats.setStatisticsEnabled( true );
|
||||
stats.clear();
|
||||
|
||||
//Try NaturalIdLoadAccess
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
|
||||
// first query
|
||||
Citizen loadedCitizen = (Citizen) s.get( Citizen.class, citizen.getId() );
|
||||
assertNotNull( loadedCitizen );
|
||||
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();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Try NaturalIdLoadAccess after load
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
State france = BasicTransactionalTestCase.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();
|
||||
|
||||
// first query
|
||||
Citizen loadedCitizen = (Citizen) naturalIdLoader.load();
|
||||
assertNotNull( loadedCitizen );
|
||||
assertEquals( "NaturalId Cache Hits", 1, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "NaturalId Cache Puts", 0, stats.getNaturalIdCachePutCount() );
|
||||
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
|
||||
|
||||
// cleanup
|
||||
tx.rollback();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void saveSomeCitizens() throws Exception {
|
||||
final Citizen c1 = new Citizen();
|
||||
c1.setFirstname( "Emmanuel" );
|
||||
c1.setLastname( "Bernard" );
|
||||
c1.setSsn( "1234" );
|
||||
|
||||
final State france = new State();
|
||||
france.setName( "Ile de France" );
|
||||
c1.setState( france );
|
||||
|
||||
final Citizen c2 = new Citizen();
|
||||
c2.setFirstname( "Gavin" );
|
||||
c2.setLastname( "King" );
|
||||
c2.setSsn( "000" );
|
||||
final State australia = new State();
|
||||
australia.setName( "Australia" );
|
||||
c2.setState( australia );
|
||||
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
s.persist( australia );
|
||||
s.persist( france );
|
||||
s.persist( c1 );
|
||||
s.persist( c2 );
|
||||
tx.commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
69
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Citizen.java
vendored
Normal file
69
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Citizen.java
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
//$Id$
|
||||
package org.hibernate.test.cache.infinispan.functional;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.NaturalIdCache;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Entity
|
||||
@NaturalIdCache
|
||||
public class Citizen {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
@NaturalId
|
||||
@ManyToOne
|
||||
private State state;
|
||||
@NaturalId
|
||||
private String ssn;
|
||||
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
|
||||
public void setFirstname(String firstname) {
|
||||
this.firstname = firstname;
|
||||
}
|
||||
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
|
||||
public void setLastname(String lastname) {
|
||||
this.lastname = lastname;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getSsn() {
|
||||
return ssn;
|
||||
}
|
||||
|
||||
public void setSsn(String ssn) {
|
||||
this.ssn = ssn;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package org.hibernate.test.cache.infinispan.functional;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.NaturalIdCache;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
@NaturalIdCache
|
||||
/**
|
||||
* Test case for NaturalId annotation - ANN-750
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
* @author Hardy Ferentschik
|
||||
*/
|
||||
public class NaturalIdOnManyToOne {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
int id;
|
||||
|
||||
@NaturalId
|
||||
@ManyToOne
|
||||
Citizen citizen;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Citizen getCitizen() {
|
||||
return citizen;
|
||||
}
|
||||
|
||||
public void setCitizen(Citizen citizen) {
|
||||
this.citizen = citizen;
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
|||
*/
|
||||
public abstract class SingleNodeTestCase extends BaseCoreFunctionalTestCase {
|
||||
private static final Log log = LogFactory.getLog( SingleNodeTestCase.class );
|
||||
private TransactionManager tm;
|
||||
protected TransactionManager tm;
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
|
|
32
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/State.java
vendored
Normal file
32
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/State.java
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
//$Id$
|
||||
package org.hibernate.test.cache.infinispan.functional;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Entity
|
||||
public class State {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
package org.hibernate.test.cache.infinispan.functional.cluster;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.cache.spi.NaturalIdCacheKey;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.test.cache.infinispan.functional.Citizen;
|
||||
import org.hibernate.test.cache.infinispan.functional.NaturalIdOnManyToOne;
|
||||
import org.hibernate.test.cache.infinispan.functional.State;
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.manager.CacheContainer;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent;
|
||||
import org.infinispan.util.logging.Log;
|
||||
import org.infinispan.util.logging.LogFactory;
|
||||
import org.jboss.util.collection.ConcurrentSet;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static org.infinispan.test.TestingUtil.tmpDirectory;
|
||||
import static org.infinispan.test.TestingUtil.withTx;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* // TODO: Document this
|
||||
*
|
||||
* @author Galder Zamarreño
|
||||
* @since // TODO
|
||||
*/
|
||||
public class NaturalIdInvalidationTestCase extends DualNodeTestCase {
|
||||
|
||||
private static final Log log = LogFactory.getLog(NaturalIdInvalidationTestCase.class);
|
||||
|
||||
private static final long SLEEP_TIME = 50l;
|
||||
private static final Integer CUSTOMER_ID = new Integer( 1 );
|
||||
private static int test = 0;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Citizen.class, State.class,
|
||||
NaturalIdOnManyToOne.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAll() throws Exception {
|
||||
log.info( "*** testAll()" );
|
||||
|
||||
// Bind a listener to the "local" cache
|
||||
// Our region factory makes its CacheManager available to us
|
||||
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTestCase.LOCAL);
|
||||
Cache localNaturalIdCache = localManager.getCache(Citizen.class.getName() + "##NaturalId");
|
||||
MyListener localListener = new MyListener( "local" );
|
||||
localNaturalIdCache.addListener(localListener);
|
||||
TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestCase.LOCAL);
|
||||
|
||||
// Bind a listener to the "remote" cache
|
||||
CacheContainer remoteManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTestCase.REMOTE);
|
||||
Cache remoteNaturalIdCache = remoteManager.getCache(Citizen.class.getName() + "##NaturalId");
|
||||
MyListener remoteListener = new MyListener( "remote" );
|
||||
remoteNaturalIdCache.addListener(remoteListener);
|
||||
TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestCase.REMOTE);
|
||||
|
||||
SessionFactory localFactory = sessionFactory();
|
||||
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
|
||||
|
||||
try {
|
||||
assertTrue(remoteListener.isEmpty());
|
||||
assertTrue(localListener.isEmpty());
|
||||
|
||||
saveSomeCitizens(localTM, localFactory);
|
||||
|
||||
assertTrue(remoteListener.isEmpty());
|
||||
assertTrue(localListener.isEmpty());
|
||||
|
||||
// Sleep a bit to let async commit propagate. Really just to
|
||||
// help keep the logs organized for debugging any issues
|
||||
sleep( SLEEP_TIME );
|
||||
|
||||
log.debug("Find node 0");
|
||||
// This actually brings the collection into the cache
|
||||
getCitizenWithCriteria(localTM, localFactory);
|
||||
|
||||
sleep( SLEEP_TIME );
|
||||
// Now the collection is in the cache so, the 2nd "get"
|
||||
// should read everything from the cache
|
||||
log.debug( "Find(2) node 0" );
|
||||
localListener.clear();
|
||||
getCitizenWithCriteria(localTM, localFactory);
|
||||
|
||||
// Check the read came from the cache
|
||||
log.debug( "Check cache 0" );
|
||||
assertLoadedFromCache(localListener, "1234");
|
||||
|
||||
log.debug( "Find node 1" );
|
||||
// This actually brings the collection into the cache since invalidation is in use
|
||||
getCitizenWithCriteria(remoteTM, remoteFactory);
|
||||
|
||||
// Now the collection is in the cache so, the 2nd "get"
|
||||
// should read everything from the cache
|
||||
log.debug( "Find(2) node 1" );
|
||||
remoteListener.clear();
|
||||
getCitizenWithCriteria(remoteTM, remoteFactory);
|
||||
|
||||
// Check the read came from the cache
|
||||
log.debug( "Check cache 1" );
|
||||
assertLoadedFromCache(remoteListener, "1234");
|
||||
|
||||
// Modify customer in remote
|
||||
remoteListener.clear();
|
||||
deleteCitizenWithCriteria(remoteTM, remoteFactory);
|
||||
sleep(250);
|
||||
|
||||
Set localKeys = localNaturalIdCache.keySet();
|
||||
assertEquals(1, localKeys.size());
|
||||
// Only key left is the one for the citizen *not* in France
|
||||
localKeys.toString().contains("000");
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Error", e);
|
||||
throw e;
|
||||
} finally {
|
||||
withTx(localTM, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = sessionFactory().openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
|
||||
s.createQuery( "delete Citizen" ).executeUpdate();
|
||||
s.createQuery( "delete State" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void assertLoadedFromCache(MyListener localListener, String id) {
|
||||
for (String visited : localListener.visited){
|
||||
if (visited.contains(id))
|
||||
return;
|
||||
}
|
||||
fail("Citizen (" + id + ") should have present in the cache");
|
||||
}
|
||||
|
||||
private void saveSomeCitizens(TransactionManager tm, final SessionFactory sf) throws Exception {
|
||||
final Citizen c1 = new Citizen();
|
||||
c1.setFirstname( "Emmanuel" );
|
||||
c1.setLastname( "Bernard" );
|
||||
c1.setSsn( "1234" );
|
||||
|
||||
final State france = new State();
|
||||
france.setName( "Ile de France" );
|
||||
c1.setState( france );
|
||||
|
||||
final Citizen c2 = new Citizen();
|
||||
c2.setFirstname( "Gavin" );
|
||||
c2.setLastname( "King" );
|
||||
c2.setSsn( "000" );
|
||||
final State australia = new State();
|
||||
australia.setName( "Australia" );
|
||||
c2.setState( australia );
|
||||
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = sf.openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
s.persist( australia );
|
||||
s.persist( france );
|
||||
s.persist( c1 );
|
||||
s.persist( c2 );
|
||||
tx.commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getCitizenWithCriteria(TransactionManager tm, final SessionFactory sf) throws Exception {
|
||||
withTx(tm, new Callable<Void >() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = sf.openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
State france = getState(s, "Ile de France");
|
||||
Criteria criteria = s.createCriteria( Citizen.class );
|
||||
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
|
||||
criteria.setCacheable( true );
|
||||
criteria.list();
|
||||
// cleanup
|
||||
tx.commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteCitizenWithCriteria(TransactionManager tm, final SessionFactory sf) throws Exception {
|
||||
withTx(tm, new Callable<Void >() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = sf.openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
State france = getState(s, "Ile de France");
|
||||
Criteria criteria = s.createCriteria( Citizen.class );
|
||||
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
|
||||
criteria.setCacheable( true );
|
||||
Citizen c = (Citizen) criteria.uniqueResult();
|
||||
s.delete(c);
|
||||
// cleanup
|
||||
tx.commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
@Listener
|
||||
public static class MyListener {
|
||||
private static final Log log = LogFactory.getLog( MyListener.class );
|
||||
private Set<String> visited = new ConcurrentSet<String>();
|
||||
private final String name;
|
||||
|
||||
public MyListener(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
visited.clear();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return visited.isEmpty();
|
||||
}
|
||||
|
||||
@CacheEntryVisited
|
||||
public void nodeVisited(CacheEntryVisitedEvent event) {
|
||||
log.debug( event.toString() );
|
||||
if ( !event.isPre() ) {
|
||||
NaturalIdCacheKey cacheKey = (NaturalIdCacheKey) event.getKey();
|
||||
visited.add(cacheKey.toString());
|
||||
// Integer primKey = (Integer) cacheKey.getKey();
|
||||
// String key = (String) cacheKey.getEntityOrRoleName() + '#' + primKey;
|
||||
// log.debug( "MyListener[" + name + "] - Visiting key " + key );
|
||||
// // String name = fqn.toString();
|
||||
// String token = ".functional.";
|
||||
// int index = key.indexOf( token );
|
||||
// if ( index > -1 ) {
|
||||
// index += token.length();
|
||||
// key = key.substring( index );
|
||||
// log.debug( "MyListener[" + name + "] - recording visit to " + key );
|
||||
// visited.add( key );
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue