HHH-6974 Addition of NaturalIdRegion SPI

Adds NaturalIdRegion to RegionFactory
Adds creation of NaturalIdRegions to SessionFactoryImpl setup
Basic copy of collection cache region behavior for first pass at naturalId region support in ehcache
This commit is contained in:
Eric Dalquist 2012-01-27 13:17:49 -06:00 committed by Steve Ebersole
parent 6702688c1f
commit 72fe79a3f2
19 changed files with 227 additions and 51 deletions

View File

@ -44,4 +44,11 @@ public @interface NaturalId {
* @return {@code true} indicates the natural id is mutable; {@code false} (the default) that it is immutable.
*/
boolean mutable() default false;
/**
* Should the mapping of this natural id to the primary id be cached
*
* @return {@code true} (the default) indicates the natural id mapping should be cached; {@code false} that the mapping should not be cached.
*/
boolean cache() default true;
}

View File

@ -30,6 +30,7 @@ import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.PluralAttributeBinding;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.VersionType;
/**
@ -90,6 +91,14 @@ public class CacheDataDescriptionImpl implements CacheDataDescription {
);
}
public static CacheDataDescriptionImpl decode(EntityPersister persister) {
return new CacheDataDescriptionImpl(
!persister.getEntityMetamodel().hasImmutableNaturalId(),
false,
null
);
}
private static Comparator getVersionComparator(EntityBinding model ) {
Comparator versionComparator = null;
if ( model.isVersioned() ) {

View File

@ -30,6 +30,7 @@ import org.hibernate.cache.NoCachingEnabledException;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
@ -68,6 +69,11 @@ public class NoCachingRegionFactory implements RegionFactory {
throw new NoCachingEnabledException();
}
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
throw new NoCachingEnabledException();
}
public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
throw new NoCachingEnabledException();

View File

@ -106,6 +106,20 @@ public interface RegionFactory extends Service {
public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException;
/**
* Build a cache region specialized for storing NaturalId to Primary Key mappings.
*
* @param regionName The name of the region.
* @param properties Configuration properties.
* @param metadata Information regarding the type of data to be cached
*
* @return The built region
*
* @throws CacheException Indicates problems building the region.
*/
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException;
/**
* Build a cache region specialized for storing collection data.
*

View File

@ -25,14 +25,17 @@ package org.hibernate.event.internal;
import java.io.Serializable;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.spi.ResolveNaturalIdEvent;
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.jboss.logging.Logger;
/**
* Defines the default load event listeners used by hibernate for loading entities
@ -141,48 +144,37 @@ public class DefaultResolveNaturalIdEventListener
* @return The entity from the second-level cache, or null.
*/
protected Serializable loadFromSecondLevelCache(final ResolveNaturalIdEvent event) {
// final SessionImplementor source = event.getSession();
//
// final boolean useCache = persister.hasCache()
// && source.getCacheMode().isGetEnabled();
//
// if ( useCache ) {
//
// final SessionFactoryImplementor factory = source.getFactory();
//
// final CacheKey ck = source.generateCacheKey(
// event.getNaturalIdValues(),
// persister.getIdentifierType(),
// persister.getRootEntityName()
// );
// Object ce = persister.getCacheAccessStrategy().get( ck, source.getTimestamp() );
// if ( factory.getStatistics().isStatisticsEnabled() ) {
// if ( ce == null ) {
// factory.getStatisticsImplementor().secondLevelCacheMiss(
// persister.getCacheAccessStrategy().getRegion().getName()
// );
// }
// else {
// factory.getStatisticsImplementor().secondLevelCacheHit(
// persister.getCacheAccessStrategy().getRegion().getName()
// );
// }
// }
//
// if ( ce != null ) {
// CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure( ce, factory );
//
// // Entity was found in second-level cache...
// return assembleCacheEntry(
// entry,
// event.getEntityId(),
// persister,
// event
// );
// }
// }
// final SessionImplementor source = event.getSession();
//
// EntityPersister persister = event.getEntityPersister();
//
// final boolean useCache = persister.hasCache() && source.getCacheMode().isGetEnabled();
//
// if ( useCache ) {
//
// final SessionFactoryImplementor factory = source.getFactory();
//
// final CacheKey ck = source.generateCacheKey( event.getNaturalIdValues(), persister.getIdentifierType(),
// persister.getRootEntityName() );
// Object ce = persister.getCacheAccessStrategy().get( ck, source.getTimestamp() );
// if ( factory.getStatistics().isStatisticsEnabled() ) {
// if ( ce == null ) {
// factory.getStatisticsImplementor().secondLevelCacheMiss(
// persister.getCacheAccessStrategy().getRegion().getName() );
// }
// else {
// factory.getStatisticsImplementor().secondLevelCacheHit(
// persister.getCacheAccessStrategy().getRegion().getName() );
// }
// }
//
// if ( ce != null ) {
// CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure( ce, factory );
//
// // Entity was found in second-level cache...
// return assembleCacheEntry( entry, event.getEntityId(), persister, event );
// }
// }
return null;
}

View File

@ -66,12 +66,14 @@ import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryCache;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.UpdateTimestampsCache;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.RegionAccessStrategy;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
@ -171,6 +173,7 @@ import org.hibernate.type.TypeResolver;
public final class SessionFactoryImpl
implements SessionFactoryImplementor {
private static final String NATURAL_ID_CACHE_SUFFIX = "##NaturalId";
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SessionFactoryImpl.class.getName());
private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator();
@ -356,6 +359,18 @@ public final class SessionFactoryImpl
);
entityPersisters.put( model.getEntityName(), cp );
classMeta.put( model.getEntityName(), cp.getClassMetadata() );
if ( cp.hasNaturalIdentifier() && cp.isNatrualIdentifierCached() ) {
final String naturalIdCacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName() + NATURAL_ID_CACHE_SUFFIX;
NaturalIdRegionAccessStrategy naturalIdAccessStrategy = ( NaturalIdRegionAccessStrategy ) entityAccessStrategies.get( naturalIdCacheRegionName );
if ( naturalIdAccessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
final NaturalIdRegion naturalIdRegion = settings.getRegionFactory().buildNaturalIdRegion( naturalIdCacheRegionName, properties, CacheDataDescriptionImpl.decode( cp ) );
naturalIdAccessStrategy = naturalIdRegion.buildAccessStrategy( settings.getRegionFactory().getDefaultAccessType() );
entityAccessStrategies.put( naturalIdCacheRegionName, naturalIdAccessStrategy );
allCacheRegions.put( naturalIdCacheRegionName, naturalIdRegion );
}
}
}
this.classMetadata = Collections.unmodifiableMap(classMeta);
@ -790,6 +805,14 @@ public final class SessionFactoryImpl
);
entityPersisters.put( model.getEntity().getName(), cp );
classMeta.put( model.getEntity().getName(), cp.getClassMetadata() );
if ( settings.isSecondLevelCacheEnabled() && cp.hasNaturalIdentifier() && cp.isNatrualIdentifierCached() ) {
final String naturalIdCacheRegionName = cacheRegionPrefix + rootEntityBinding.getHierarchyDetails().getCaching().getRegion() + NATURAL_ID_CACHE_SUFFIX;
final NaturalIdRegion naturalIdRegion = settings.getRegionFactory().buildNaturalIdRegion( naturalIdCacheRegionName, properties, CacheDataDescriptionImpl.decode( cp ) );
final NaturalIdRegionAccessStrategy naturalIdAccessStrategy = naturalIdRegion.buildAccessStrategy( settings.getRegionFactory().getDefaultAccessType() );
entityAccessStrategies.put( naturalIdCacheRegionName, naturalIdAccessStrategy );
allCacheRegions.put( naturalIdCacheRegionName, naturalIdRegion );
}
}
this.classMetadata = Collections.unmodifiableMap(classMeta);

View File

@ -4602,6 +4602,11 @@ public abstract class AbstractEntityPersister
return entityMetamodel.hasNaturalIdentifier();
}
@Override
public boolean isNatrualIdentifierCached() {
return entityMetamodel.isNatrualIdentifierCached();
}
public void setPropertyValue(Object object, String propertyName, Object value) {
getEntityTuplizer().setPropertyValue( object, propertyName, value );
}

View File

@ -31,6 +31,7 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.annotations.NaturalId;
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
import org.hibernate.cache.spi.OptimisticCacheSource;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
@ -289,6 +290,13 @@ public interface EntityPersister extends OptimisticCacheSource {
*/
public boolean hasNaturalIdentifier();
/**
* Determine whether this entity's natural identifier is cacheable. {@link NaturalId#cache()}
*
* @return True if the natural id is cacheable, false if it is not cacheable or no natural id is defined
*/
public boolean isNatrualIdentifierCached();
/**
* If the entity defines a natural id ({@link #hasNaturalIdentifier()}), which
* properties make up the natural id.

View File

@ -112,6 +112,7 @@ public class EntityMetamodel implements Serializable {
private final int[] naturalIdPropertyNumbers;
private final boolean hasImmutableNaturalId;
private final boolean hasCacheableNaturalId;
private boolean lazy; //not final because proxy factory creation can fail
private final boolean hasCascades;
@ -257,10 +258,12 @@ public class EntityMetamodel implements Serializable {
if (naturalIdNumbers.size()==0) {
naturalIdPropertyNumbers = null;
hasImmutableNaturalId = false;
hasCacheableNaturalId = false;
}
else {
naturalIdPropertyNumbers = ArrayHelper.toIntArray(naturalIdNumbers);
hasImmutableNaturalId = !foundUpdateableNaturalIdProperty;
hasCacheableNaturalId = true; //TODO how to read the annotation here?
}
hasInsertGeneratedValues = foundInsertGeneratedValue;
@ -502,10 +505,12 @@ public class EntityMetamodel implements Serializable {
if (naturalIdNumbers.size()==0) {
naturalIdPropertyNumbers = null;
hasImmutableNaturalId = false;
hasCacheableNaturalId = false;
}
else {
naturalIdPropertyNumbers = ArrayHelper.toIntArray(naturalIdNumbers);
hasImmutableNaturalId = !foundUpdateableNaturalIdProperty;
hasCacheableNaturalId = true; //TODO how to read the annotation here?
}
hasInsertGeneratedValues = foundInsertGeneratedValue;
@ -712,6 +717,10 @@ public class EntityMetamodel implements Serializable {
return naturalIdPropertyNumbers!=null;
}
public boolean isNatrualIdentifierCached() {
return hasNaturalIdentifier() && hasCacheableNaturalId;
}
public boolean hasImmutableNaturalId() {
return hasImmutableNaturalId;
}

View File

@ -236,6 +236,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
return false;
}
@Override
public boolean isNatrualIdentifierCached() {
return false;
}
@Override
public int[] getNaturalIdentifierProperties() {
return new int[0];

View File

@ -564,6 +564,11 @@ public class CustomPersister implements EntityPersister {
return false;
}
@Override
public boolean isNatrualIdentifierCached() {
return false;
}
@Override
public boolean hasMutableProperties() {
return false;

View File

@ -44,6 +44,7 @@ import org.hibernate.cache.ehcache.management.impl.ProviderMBeanRegistrationHelp
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
@ -130,6 +131,13 @@ abstract class AbstractEhcacheRegionFactory implements RegionFactory {
return new EhcacheEntityRegion( accessStrategyFactory, getCache( regionName ), settings, metadata, properties );
}
@Override
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
// TODO Auto-generated method stub
return null;
}
/**
* {@inheritDoc}
*/

View File

@ -25,10 +25,12 @@ package org.hibernate.cache.ehcache.internal.nonstop;
import org.hibernate.cache.ehcache.internal.regions.EhcacheCollectionRegion;
import org.hibernate.cache.ehcache.internal.regions.EhcacheEntityRegion;
import org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion;
import org.hibernate.cache.ehcache.internal.strategy.EhcacheAccessStrategyFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
/**
* Implementation of {@link org.hibernate.cache.ehcache.internal.strategy.EhcacheAccessStrategyFactory} that takes care of Nonstop cache exceptions using
@ -60,6 +62,17 @@ public class NonstopAccessStrategyFactory implements EhcacheAccessStrategyFactor
);
}
@Override
public NaturalIdRegionAccessStrategy createNaturalIdRegionAccessStrategy(EhcacheNaturalIdRegion naturalIdRegion,
AccessType accessType) {
return new NonstopAwareNaturalIdRegionAccessStrategy(
actualFactory.createNaturalIdRegionAccessStrategy(
naturalIdRegion,
accessType
), HibernateNonstopCacheExceptionHandler.getInstance()
);
}
/**
* {@inheritDoc}
*/

View File

@ -25,9 +25,11 @@ package org.hibernate.cache.ehcache.internal.strategy;
import org.hibernate.cache.ehcache.internal.regions.EhcacheCollectionRegion;
import org.hibernate.cache.ehcache.internal.regions.EhcacheEntityRegion;
import org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
/**
* Factory to create {@link EntityRegionAccessStrategy}
@ -57,5 +59,15 @@ public interface EhcacheAccessStrategyFactory {
*/
public CollectionRegionAccessStrategy createCollectionRegionAccessStrategy(EhcacheCollectionRegion collectionRegion,
AccessType accessType);
/**
* Create {@link NaturalIdRegionAccessStrategy} for the input {@link org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion} and {@link AccessType}
*
* @param naturalIdRegion
* @param accessType
*
* @return the created {@link NaturalIdRegionAccessStrategy}
*/
public NaturalIdRegionAccessStrategy createNaturalIdRegionAccessStrategy(EhcacheNaturalIdRegion naturalIdRegion,
AccessType accessType);
}

View File

@ -23,14 +23,15 @@
*/
package org.hibernate.cache.ehcache.internal.strategy;
import org.jboss.logging.Logger;
import org.hibernate.cache.ehcache.EhCacheMessageLogger;
import org.hibernate.cache.ehcache.internal.regions.EhcacheCollectionRegion;
import org.hibernate.cache.ehcache.internal.regions.EhcacheEntityRegion;
import org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.jboss.logging.Logger;
/**
* Class implementing {@link EhcacheAccessStrategyFactory}
@ -111,4 +112,37 @@ public class EhcacheAccessStrategyFactoryImpl implements EhcacheAccessStrategyFa
}
}
@Override
public NaturalIdRegionAccessStrategy createNaturalIdRegionAccessStrategy(EhcacheNaturalIdRegion naturalIdRegion,
AccessType accessType) {
switch ( accessType ) {
case READ_ONLY:
if ( naturalIdRegion.getCacheDataDescription().isMutable() ) {
LOG.readOnlyCacheConfiguredForMutableEntity( naturalIdRegion.getName() );
}
return new ReadOnlyEhcacheNaturalIdRegionAccessStrategy(
naturalIdRegion,
naturalIdRegion.getSettings()
);
case READ_WRITE:
return new ReadWriteEhcacheNaturalIdRegionAccessStrategy(
naturalIdRegion,
naturalIdRegion.getSettings()
);
case NONSTRICT_READ_WRITE:
return new NonStrictReadWriteEhcacheNaturalIdRegionAccessStrategy(
naturalIdRegion,
naturalIdRegion.getSettings()
);
case TRANSACTIONAL:
return new TransactionalEhcacheNaturalIdRegionAccessStrategy(
naturalIdRegion, naturalIdRegion.getEhcache(), naturalIdRegion
.getSettings()
);
default:
throw new IllegalArgumentException( "unrecognized access strategy type [" + accessType + "]" );
}
}
}

View File

@ -258,6 +258,11 @@ public class PersisterClassProviderTest {
return false;
}
@Override
public boolean isNatrualIdentifierCached() {
return false;
}
@Override
public int[] getNaturalIdentifierProperties() {
return new int[0];

View File

@ -16,6 +16,7 @@ import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
@ -195,6 +196,12 @@ public class InfinispanRegionFactory implements RegionFactory {
return region;
}
@Override
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
throw new UnsupportedOperationException(); //TODO
}
/**
* {@inheritDoc}
*/

View File

@ -26,6 +26,7 @@ import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
@ -102,6 +103,12 @@ public class ClusterAwareRegionFactory implements RegionFactory {
return delegate.buildEntityRegion(regionName, properties, metadata);
}
@Override
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
return delegate.buildNaturalIdRegion( regionName, properties, metadata );
}
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
throws CacheException {
return delegate.buildQueryResultsRegion(regionName, properties);

View File

@ -32,6 +32,7 @@ import org.hibernate.cache.internal.Timestamper;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
@ -86,6 +87,12 @@ public class CachingRegionFactory implements RegionFactory {
return new EntityRegionImpl( regionName, metadata, settings );
}
@Override
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
return new NaturalIdRegionImpl( regionName, metadata, settings );
}
@Override
public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {