HHH-12146 - Support enabling caching at any level within a mapped hierarchy

This commit is contained in:
Steve Ebersole 2017-12-12 21:22:43 -06:00
parent 6edff69101
commit 59c3baae32
36 changed files with 483 additions and 228 deletions

View File

@ -61,7 +61,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
for ( Queryable persister : affectedQueryables ) { for ( Queryable persister : affectedQueryables ) {
spacesList.addAll( Arrays.asList( (String[]) persister.getQuerySpaces() ) ); spacesList.addAll( Arrays.asList( (String[]) persister.getQuerySpaces() ) );
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
entityCleanups.add( new EntityCleanup( persister.getCacheAccessStrategy() ) ); entityCleanups.add( new EntityCleanup( persister.getCacheAccessStrategy() ) );
} }
if ( persister.hasNaturalIdentifier() && persister.hasNaturalIdCache() ) { if ( persister.hasNaturalIdentifier() && persister.hasNaturalIdCache() ) {
@ -105,7 +105,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
if ( affectedEntity( tableSpaces, entitySpaces ) ) { if ( affectedEntity( tableSpaces, entitySpaces ) ) {
spacesList.addAll( Arrays.asList( entitySpaces ) ); spacesList.addAll( Arrays.asList( entitySpaces ) );
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
entityCleanups.add( new EntityCleanup( persister.getCacheAccessStrategy() ) ); entityCleanups.add( new EntityCleanup( persister.getCacheAccessStrategy() ) );
} }
if ( persister.hasNaturalIdentifier() && persister.hasNaturalIdCache() ) { if ( persister.hasNaturalIdentifier() && persister.hasNaturalIdCache() ) {

View File

@ -78,7 +78,7 @@ public abstract class EntityAction
protected abstract boolean hasPostCommitEventListeners(); protected abstract boolean hasPostCommitEventListeners();
protected boolean needsAfterTransactionCompletion() { protected boolean needsAfterTransactionCompletion() {
return persister.hasCache() || hasPostCommitEventListeners(); return persister.canWriteToCache() || hasPostCommitEventListeners();
} }
/** /**

View File

@ -86,7 +86,7 @@ public class EntityDeleteAction extends EntityAction {
} }
final Object ck; final Object ck;
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
ck = cache.generateCacheKey( id, persister, session.getFactory(), session.getTenantIdentifier() ); ck = cache.generateCacheKey( id, persister, session.getFactory(), session.getTenantIdentifier() );
lock = cache.lockItem( session, ck, version ); lock = cache.lockItem( session, ck, version );
@ -113,7 +113,7 @@ public class EntityDeleteAction extends EntityAction {
persistenceContext.removeEntity( entry.getEntityKey() ); persistenceContext.removeEntity( entry.getEntityKey() );
persistenceContext.removeProxy( entry.getEntityKey() ); persistenceContext.removeProxy( entry.getEntityKey() );
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
persister.getCacheAccessStrategy().remove( session, ck); persister.getCacheAccessStrategy().remove( session, ck);
} }
@ -187,7 +187,7 @@ public class EntityDeleteAction extends EntityAction {
@Override @Override
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws HibernateException { public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws HibernateException {
EntityPersister entityPersister = getPersister(); EntityPersister entityPersister = getPersister();
if ( entityPersister.hasCache() ) { if ( entityPersister.canWriteToCache() ) {
EntityRegionAccessStrategy cache = entityPersister.getCacheAccessStrategy(); EntityRegionAccessStrategy cache = entityPersister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey( final Object ck = cache.generateCacheKey(
getId(), getId(),

View File

@ -249,7 +249,7 @@ public final class EntityInsertAction extends AbstractEntityInsertAction {
} }
private boolean isCachePutEnabled(EntityPersister persister, SharedSessionContractImplementor session) { private boolean isCachePutEnabled(EntityPersister persister, SharedSessionContractImplementor session) {
return persister.hasCache() return persister.canWriteToCache()
&& !persister.isCacheInvalidationRequired() && !persister.isCacheInvalidationRequired()
&& session.getCacheMode().isPutEnabled(); && session.getCacheMode().isPutEnabled();
} }

View File

@ -127,7 +127,7 @@ public final class EntityUpdateAction extends EntityAction {
} }
final Object ck; final Object ck;
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
ck = cache.generateCacheKey( ck = cache.generateCacheKey(
id, id,
@ -184,7 +184,7 @@ public final class EntityUpdateAction extends EntityAction {
entry.postUpdate( instance, state, nextVersion ); entry.postUpdate( instance, state, nextVersion );
} }
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
if ( persister.isCacheInvalidationRequired() || entry.getStatus()!= Status.MANAGED ) { if ( persister.isCacheInvalidationRequired() || entry.getStatus()!= Status.MANAGED ) {
persister.getCacheAccessStrategy().remove( session, ck); persister.getCacheAccessStrategy().remove( session, ck);
} }
@ -310,7 +310,7 @@ public final class EntityUpdateAction extends EntityAction {
@Override @Override
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws CacheException { public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws CacheException {
final EntityPersister persister = getPersister(); final EntityPersister persister = getPersister();
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey( final Object ck = cache.generateCacheKey(
getId(), getId(),

View File

@ -2146,6 +2146,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
"Cache override referenced a non-root entity : " + cacheRegionDefinition.getRole() "Cache override referenced a non-root entity : " + cacheRegionDefinition.getRole()
); );
} }
entityBinding.setCached( true );
( (RootClass) entityBinding ).setCacheRegionName( cacheRegionDefinition.getRegion() ); ( (RootClass) entityBinding ).setCacheRegionName( cacheRegionDefinition.getRegion() );
( (RootClass) entityBinding ).setCacheConcurrencyStrategy( cacheRegionDefinition.getUsage() ); ( (RootClass) entityBinding ).setCacheConcurrencyStrategy( cacheRegionDefinition.getUsage() );
( (RootClass) entityBinding ).setLazyPropertiesCacheable( cacheRegionDefinition.isCacheLazy() ); ( (RootClass) entityBinding ).setLazyPropertiesCacheable( cacheRegionDefinition.isCacheLazy() );

View File

@ -331,7 +331,7 @@ public class ModelBinder {
} }
rootEntityDescriptor.setCacheRegionName( caching.getRegion() ); rootEntityDescriptor.setCacheRegionName( caching.getRegion() );
rootEntityDescriptor.setLazyPropertiesCacheable( caching.isCacheLazyProperties() ); rootEntityDescriptor.setLazyPropertiesCacheable( caching.isCacheLazyProperties() );
rootEntityDescriptor.setCachingExplicitlyRequested( caching.getRequested() != TruthValue.UNKNOWN ); rootEntityDescriptor.setCached( caching.getRequested() != TruthValue.UNKNOWN );
} }
private void bindEntityIdentifier( private void bindEntityIdentifier(

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.cfg; package org.hibernate.cfg;
import java.lang.annotation.Annotation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -21,7 +20,6 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import javax.persistence.Basic; import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ConstraintMode; import javax.persistence.ConstraintMode;
@ -78,7 +76,6 @@ import org.hibernate.FetchMode;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache; import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade; import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Check; import org.hibernate.annotations.Check;
@ -105,7 +102,6 @@ import org.hibernate.annotations.ListIndexBase;
import org.hibernate.annotations.ManyToAny; import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyType; import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDelete;
@ -625,8 +621,7 @@ public final class AnnotationBinder {
entityBinder.setProxy( clazzToProcess.getAnnotation( Proxy.class ) ); entityBinder.setProxy( clazzToProcess.getAnnotation( Proxy.class ) );
entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) ); entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) ); entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) );
entityBinder.setCache( determineCacheSettings( clazzToProcess, context ) ); applyCacheSettings( entityBinder, clazzToProcess, context );
entityBinder.setNaturalIdCache( clazzToProcess, clazzToProcess.getAnnotation( NaturalIdCache.class ) );
bindFilters( clazzToProcess, entityBinder, context ); bindFilters( clazzToProcess, entityBinder, context );
@ -1158,80 +1153,19 @@ public final class AnnotationBinder {
} }
} }
private static Cache determineCacheSettings(XClass clazzToProcess, MetadataBuildingContext context) { private static void applyCacheSettings(EntityBinder binder, XClass clazzToProcess, MetadataBuildingContext context) {
Cache cacheAnn = clazzToProcess.getAnnotation( Cache.class ); binder.applyCaching(
if ( cacheAnn != null ) { clazzToProcess,
return cacheAnn; determineSharedCacheMode( context ),
} context
Cacheable cacheableAnn = clazzToProcess.getAnnotation( Cacheable.class ); );
SharedCacheMode mode = determineSharedCacheMode( context );
switch ( mode ) {
case ALL: {
cacheAnn = buildCacheMock( clazzToProcess.getName(), context );
break;
}
case ENABLE_SELECTIVE: {
if ( cacheableAnn != null && cacheableAnn.value() ) {
cacheAnn = buildCacheMock( clazzToProcess.getName(), context );
}
break;
}
case DISABLE_SELECTIVE: {
if ( cacheableAnn == null || cacheableAnn.value() ) {
cacheAnn = buildCacheMock( clazzToProcess.getName(), context );
}
break;
}
default: {
// treat both NONE and UNSPECIFIED the same
break;
}
}
return cacheAnn;
} }
private static SharedCacheMode determineSharedCacheMode(MetadataBuildingContext context) { private static SharedCacheMode determineSharedCacheMode(MetadataBuildingContext context) {
return context.getBuildingOptions().getSharedCacheMode(); return context.getBuildingOptions().getSharedCacheMode();
} }
private static Cache buildCacheMock(String region, MetadataBuildingContext context) {
return new LocalCacheAnnotationImpl( region, determineCacheConcurrencyStrategy( context ) );
}
private static CacheConcurrencyStrategy determineCacheConcurrencyStrategy(MetadataBuildingContext context) {
return CacheConcurrencyStrategy.fromAccessType(
context.getBuildingOptions().getImplicitCacheAccessType()
);
}
@SuppressWarnings({ "ClassExplicitlyAnnotation" })
private static class LocalCacheAnnotationImpl implements Cache {
private final String region;
private final CacheConcurrencyStrategy usage;
private LocalCacheAnnotationImpl(String region, CacheConcurrencyStrategy usage) {
this.region = region;
this.usage = usage;
}
public CacheConcurrencyStrategy usage() {
return usage;
}
public String region() {
return region;
}
public String include() {
return "all";
}
public Class<? extends Annotation> annotationType() {
return Cache.class;
}
}
private static PersistentClass makePersistentClass( private static PersistentClass makePersistentClass(
InheritanceState inheritanceState, InheritanceState inheritanceState,
PersistentClass superEntity, PersistentClass superEntity,

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.cfg.annotations; package org.hibernate.cfg.annotations;
import java.lang.annotation.Annotation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -15,7 +16,6 @@ import javax.persistence.Access;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.ConstraintMode; import javax.persistence.ConstraintMode;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import javax.persistence.NamedEntityGraph; import javax.persistence.NamedEntityGraph;
@ -23,6 +23,7 @@ import javax.persistence.NamedEntityGraphs;
import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.SecondaryTable; import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables; import javax.persistence.SecondaryTables;
import javax.persistence.SharedCacheMode;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
@ -127,17 +128,19 @@ public class EntityBinder {
// atm we use both from here; HBM binding solely uses InFlightMetadataCollector.EntityTableXref // atm we use both from here; HBM binding solely uses InFlightMetadataCollector.EntityTableXref
private java.util.Map<String, Join> secondaryTables = new HashMap<String, Join>(); private java.util.Map<String, Join> secondaryTables = new HashMap<String, Join>();
private java.util.Map<String, Object> secondaryTableJoins = new HashMap<String, Object>(); private java.util.Map<String, Object> secondaryTableJoins = new HashMap<String, Object>();
private String cacheConcurrentStrategy;
private String cacheRegion;
private String naturalIdCacheRegion;
private List<Filter> filters = new ArrayList<Filter>(); private List<Filter> filters = new ArrayList<Filter>();
private InheritanceState inheritanceState; private InheritanceState inheritanceState;
private boolean ignoreIdAnnotations; private boolean ignoreIdAnnotations;
private boolean cacheLazyProperty;
private AccessType propertyAccessType = AccessType.DEFAULT; private AccessType propertyAccessType = AccessType.DEFAULT;
private boolean wrapIdsInEmbeddedComponents; private boolean wrapIdsInEmbeddedComponents;
private String subselect; private String subselect;
private boolean isCached;
private String cacheConcurrentStrategy;
private String cacheRegion;
private boolean cacheLazyProperty;
private String naturalIdCacheRegion;
public boolean wrapIdsInEmbeddedComponents() { public boolean wrapIdsInEmbeddedComponents() {
return wrapIdsInEmbeddedComponents; return wrapIdsInEmbeddedComponents;
} }
@ -281,18 +284,26 @@ public class EntityBinder {
} }
rootClass.setMutable( mutable ); rootClass.setMutable( mutable );
rootClass.setExplicitPolymorphism( isExplicitPolymorphism( polymorphismType ) ); rootClass.setExplicitPolymorphism( isExplicitPolymorphism( polymorphismType ) );
if ( StringHelper.isNotEmpty( where ) ) rootClass.setWhere( where );
if ( StringHelper.isNotEmpty( where ) ) {
rootClass.setWhere( where );
}
if ( cacheConcurrentStrategy != null ) { if ( cacheConcurrentStrategy != null ) {
rootClass.setCacheConcurrencyStrategy( cacheConcurrentStrategy ); rootClass.setCacheConcurrencyStrategy( cacheConcurrentStrategy );
rootClass.setCacheRegionName( cacheRegion ); rootClass.setCacheRegionName( cacheRegion );
rootClass.setLazyPropertiesCacheable( cacheLazyProperty ); rootClass.setLazyPropertiesCacheable( cacheLazyProperty );
} }
rootClass.setNaturalIdCacheRegionName( naturalIdCacheRegion ); rootClass.setNaturalIdCacheRegionName( naturalIdCacheRegion );
boolean forceDiscriminatorInSelects = forceDiscriminator == null boolean forceDiscriminatorInSelects = forceDiscriminator == null
? context.getBuildingOptions().shouldImplicitlyForceDiscriminatorInSelect() ? context.getBuildingOptions().shouldImplicitlyForceDiscriminatorInSelect()
: forceDiscriminator; : forceDiscriminator;
rootClass.setForceDiscriminator( forceDiscriminatorInSelects ); rootClass.setForceDiscriminator( forceDiscriminatorInSelects );
if( insertableDiscriminator != null) {
if ( insertableDiscriminator != null ) {
rootClass.setDiscriminatorInsertable( insertableDiscriminator ); rootClass.setDiscriminatorInsertable( insertableDiscriminator );
} }
} }
@ -303,11 +314,10 @@ public class EntityBinder {
if (annotatedClass.isAnnotationPresent(Immutable.class)) { if (annotatedClass.isAnnotationPresent(Immutable.class)) {
LOG.immutableAnnotationOnNonRoot(annotatedClass.getName()); LOG.immutableAnnotationOnNonRoot(annotatedClass.getName());
} }
if ( annotatedClass.isAnnotationPresent( Cacheable.class ) ||
annotatedClass.isAnnotationPresent( Cache.class ) ) {
LOG.cacheOrCacheableAnnotationOnNonRoot( annotatedClass.getName() );
}
} }
persistentClass.setCached( isCached );
persistentClass.setOptimisticLockStyle( getVersioning( optimisticLockType ) ); persistentClass.setOptimisticLockStyle( getVersioning( optimisticLockType ) );
persistentClass.setSelectBeforeUpdate( selectBeforeUpdate ); persistentClass.setSelectBeforeUpdate( selectBeforeUpdate );
@ -542,6 +552,164 @@ public class EntityBinder {
this.wrapIdsInEmbeddedComponents = wrapIdsInEmbeddedComponents; this.wrapIdsInEmbeddedComponents = wrapIdsInEmbeddedComponents;
} }
public void applyCaching(
XClass clazzToProcess,
SharedCacheMode sharedCacheMode,
MetadataBuildingContext context) {
final Cache explicitCacheAnn = clazzToProcess.getAnnotation( Cache.class );
final Cacheable explicitCacheableAnn = clazzToProcess.getAnnotation( Cacheable.class );
isCached = false;
cacheConcurrentStrategy = null;
cacheRegion = null;
cacheLazyProperty = true;
if ( persistentClass instanceof RootClass ) {
Cache effectiveCacheAnn = explicitCacheAnn;
if ( explicitCacheAnn != null ) {
// preserve legacy behavior of circumventing SharedCacheMode when Hibernate's @Cache is used.
isCached = true;
}
else {
effectiveCacheAnn = buildCacheMock( clazzToProcess.getName(), context );
switch ( sharedCacheMode ) {
case ALL: {
// all entities should be cached
isCached = true;
break;
}
case ENABLE_SELECTIVE: {
if ( explicitCacheableAnn != null && explicitCacheableAnn.value() ) {
isCached = true;
}
break;
}
case DISABLE_SELECTIVE: {
if ( explicitCacheableAnn == null || explicitCacheableAnn.value() ) {
isCached = true;
}
break;
}
default: {
// treat both NONE and UNSPECIFIED the same
isCached = false;
break;
}
}
}
cacheConcurrentStrategy = effectiveCacheAnn.usage().name();
cacheRegion = effectiveCacheAnn.region();
switch ( effectiveCacheAnn.include().toLowerCase( Locale.ROOT ) ) {
case "all": {
cacheLazyProperty = true;
break;
}
case "non-lazy": {
cacheLazyProperty = false;
break;
}
default: {
throw new AnnotationException(
"Unknown @Cache.include value [" + effectiveCacheAnn.include() + "] : "
+ annotatedClass.getName()
);
}
}
}
else {
if ( explicitCacheAnn != null ) {
LOG.cacheOrCacheableAnnotationOnNonRoot( persistentClass.getClassName() );
}
else if ( explicitCacheableAnn == null && persistentClass.getSuperclass() != null ) {
// we should inherit our super's caching config
isCached = persistentClass.getSuperclass().isCached();
}
else {
switch ( sharedCacheMode ) {
case ALL: {
// all entities should be cached
isCached = true;
break;
}
case ENABLE_SELECTIVE: {
// only entities with @Cacheable(true) should be cached
if ( explicitCacheableAnn != null && explicitCacheableAnn.value() ) {
isCached = true;
}
break;
}
case DISABLE_SELECTIVE: {
if ( explicitCacheableAnn == null || !explicitCacheableAnn.value() ) {
isCached = true;
}
break;
}
default: {
// treat both NONE and UNSPECIFIED the same
isCached = false;
break;
}
}
}
}
naturalIdCacheRegion = null;
final NaturalIdCache naturalIdCacheAnn = clazzToProcess.getAnnotation( NaturalIdCache.class );
if ( naturalIdCacheAnn != null ) {
if ( BinderHelper.isEmptyAnnotationValue( naturalIdCacheAnn.region() ) ) {
if ( explicitCacheAnn != null && StringHelper.isNotEmpty( explicitCacheAnn.region() ) ) {
naturalIdCacheRegion = explicitCacheAnn.region() + NATURAL_ID_CACHE_SUFFIX;
}
else {
naturalIdCacheRegion = clazzToProcess.getName() + NATURAL_ID_CACHE_SUFFIX;
}
}
else {
naturalIdCacheRegion = naturalIdCacheAnn.region();
}
}
}
private static Cache buildCacheMock(String region, MetadataBuildingContext context) {
return new LocalCacheAnnotationStub( region, determineCacheConcurrencyStrategy( context ) );
}
@SuppressWarnings({ "ClassExplicitlyAnnotation" })
private static class LocalCacheAnnotationStub implements Cache {
private final String region;
private final CacheConcurrencyStrategy usage;
private LocalCacheAnnotationStub(String region, CacheConcurrencyStrategy usage) {
this.region = region;
this.usage = usage;
}
public CacheConcurrencyStrategy usage() {
return usage;
}
public String region() {
return region;
}
public String include() {
return "all";
}
public Class<? extends Annotation> annotationType() {
return Cache.class;
}
}
private static CacheConcurrencyStrategy determineCacheConcurrencyStrategy(MetadataBuildingContext context) {
return CacheConcurrencyStrategy.fromAccessType(
context.getBuildingOptions().getImplicitCacheAccessType()
);
}
private static class EntityTableObjectNameSource implements ObjectNameSource { private static class EntityTableObjectNameSource implements ObjectNameSource {
private final String explicitName; private final String explicitName;
@ -1044,48 +1212,6 @@ public class EntityBinder {
return secondaryTables; return secondaryTables;
} }
public void setCache(Cache cacheAnn) {
if ( cacheAnn != null ) {
cacheRegion = BinderHelper.isEmptyAnnotationValue( cacheAnn.region() ) ?
null :
cacheAnn.region();
cacheConcurrentStrategy = getCacheConcurrencyStrategy( cacheAnn.usage() );
if ( "all".equalsIgnoreCase( cacheAnn.include() ) ) {
cacheLazyProperty = true;
}
else if ( "non-lazy".equalsIgnoreCase( cacheAnn.include() ) ) {
cacheLazyProperty = false;
}
else {
throw new AnnotationException( "Unknown lazy property annotations: " + cacheAnn.include() );
}
}
else {
cacheConcurrentStrategy = null;
cacheRegion = null;
cacheLazyProperty = true;
}
}
public void setNaturalIdCache(XClass clazzToProcess, NaturalIdCache naturalIdCacheAnn) {
if ( naturalIdCacheAnn != null ) {
if ( BinderHelper.isEmptyAnnotationValue( naturalIdCacheAnn.region() ) ) {
if (cacheRegion != null) {
naturalIdCacheRegion = cacheRegion + NATURAL_ID_CACHE_SUFFIX;
}
else {
naturalIdCacheRegion = clazzToProcess.getName() + NATURAL_ID_CACHE_SUFFIX;
}
}
else {
naturalIdCacheRegion = naturalIdCacheAnn.region();
}
}
else {
naturalIdCacheRegion = null;
}
}
public static String getCacheConcurrencyStrategy(CacheConcurrencyStrategy strategy) { public static String getCacheConcurrencyStrategy(CacheConcurrencyStrategy strategy) {
org.hibernate.cache.spi.access.AccessType accessType = strategy.toAccessType(); org.hibernate.cache.spi.access.AccessType accessType = strategy.toAccessType();
return accessType == null ? null : accessType.getExternalName(); return accessType == null ? null : accessType.getExternalName();

View File

@ -1670,7 +1670,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
@Override @Override
public void registerInsertedKey(EntityPersister persister, Serializable id) { public void registerInsertedKey(EntityPersister persister, Serializable id) {
// we only are worried about registering these if the persister defines caching // we only are worried about registering these if the persister defines caching
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
if ( insertedKeysMap == null ) { if ( insertedKeysMap == null ) {
insertedKeysMap = new HashMap<>(); insertedKeysMap = new HashMap<>();
} }
@ -1687,7 +1687,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
@Override @Override
public boolean wasInsertedDuringTransaction(EntityPersister persister, Serializable id) { public boolean wasInsertedDuringTransaction(EntityPersister persister, Serializable id) {
// again, we only really care if the entity is cached // again, we only really care if the entity is cached
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
if ( insertedKeysMap != null ) { if ( insertedKeysMap != null ) {
final List<Serializable> insertedEntityIds = insertedKeysMap.get( persister.getRootEntityName() ); final List<Serializable> insertedEntityIds = insertedKeysMap.get( persister.getRootEntityName() );
if ( insertedEntityIds != null ) { if ( insertedEntityIds != null ) {

View File

@ -183,7 +183,7 @@ public final class TwoPhaseLoad {
persister.setPropertyValues( entity, hydratedState ); persister.setPropertyValues( entity, hydratedState );
final SessionFactoryImplementor factory = session.getFactory(); final SessionFactoryImplementor factory = session.getFactory();
if ( persister.hasCache() && session.getCacheMode().isPutEnabled() ) { if ( persister.canWriteToCache() && session.getCacheMode().isPutEnabled() ) {
if ( debugEnabled ) { if ( debugEnabled ) {
LOG.debugf( LOG.debugf(

View File

@ -215,7 +215,7 @@ public class BatchFetchQueue {
private boolean isCached(EntityKey entityKey, EntityPersister persister) { private boolean isCached(EntityKey entityKey, EntityPersister persister) {
final SharedSessionContractImplementor session = context.getSession(); final SharedSessionContractImplementor session = context.getSession();
if ( context.getSession().getCacheMode().isGetEnabled() && persister.hasCache() ) { if ( context.getSession().getCacheMode().isGetEnabled() && persister.canReadFromCache() ) {
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
final Object key = cache.generateCacheKey( final Object key = cache.generateCacheKey(
entityKey.getIdentifier(), entityKey.getIdentifier(),

View File

@ -61,7 +61,7 @@ public abstract class AbstractLockUpgradeEventListener extends AbstractReassocia
); );
} }
final boolean cachingEnabled = persister.hasCache(); final boolean cachingEnabled = persister.canWriteToCache();
SoftLock lock = null; SoftLock lock = null;
Object ck = null; Object ck = null;
try { try {

View File

@ -385,7 +385,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
SoftLock lock = null; SoftLock lock = null;
final Object ck; final Object ck;
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
ck = cache.generateCacheKey( ck = cache.generateCacheKey(
event.getEntityId(), event.getEntityId(),
persister, persister,
@ -403,7 +403,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
entity = load( event, persister, keyToLoad, options ); entity = load( event, persister, keyToLoad, options );
} }
finally { finally {
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
cache.unlockItem( source, ck, lock ); cache.unlockItem( source, ck, lock );
} }
} }
@ -583,7 +583,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
final EntityKey entityKey) { final EntityKey entityKey) {
final SessionImplementor source = event.getSession(); final SessionImplementor source = event.getSession();
final boolean useCache = persister.hasCache() final boolean useCache = persister.canReadFromCache()
&& source.getCacheMode().isGetEnabled() && source.getCacheMode().isGetEnabled()
&& event.getLockMode().lessThan( LockMode.READ ); && event.getLockMode().lessThan( LockMode.READ );

View File

@ -144,7 +144,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
} }
} }
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
Object previousVersion = null; Object previousVersion = null;
if ( persister.isVersionPropertyGenerated() ) { if ( persister.isVersionPropertyGenerated() ) {
// we need to grab the version value from the entity, otherwise // we need to grab the version value from the entity, otherwise

View File

@ -629,15 +629,6 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
this.withClauseFragment = withClauseFragment; this.withClauseFragment = withClauseFragment;
} }
public boolean hasCacheablePersister() {
if ( getQueryableCollection() != null ) {
return getQueryableCollection().hasCache();
}
else {
return getQueryable().hasCache();
}
}
public void handlePropertyBeingDereferenced(Type propertySource, String propertyName) { public void handlePropertyBeingDereferenced(Type propertySource, String propertyName) {
if ( getQueryableCollection() != null && CollectionProperties.isCollectionProperty( propertyName ) ) { if ( getQueryableCollection() != null && CollectionProperties.isCollectionProperty( propertyName ) ) {
// propertyName refers to something like collection.size... // propertyName refers to something like collection.size...

View File

@ -38,6 +38,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
@ -119,7 +120,7 @@ public class CacheImpl implements CacheImplementor {
@Override @Override
public boolean containsEntity(String entityName, Serializable identifier) { public boolean containsEntity(String entityName, Serializable identifier) {
EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName ); EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName );
if ( p.hasCache() ) { if ( p.canReadFromCache() ) {
EntityRegionAccessStrategy cache = p.getCacheAccessStrategy(); EntityRegionAccessStrategy cache = p.getCacheAccessStrategy();
Object key = cache.generateCacheKey( identifier, p, sessionFactory, null ); // have to assume non tenancy Object key = cache.generateCacheKey( identifier, p, sessionFactory, null ); // have to assume non tenancy
return cache.getRegion().contains( key ); return cache.getRegion().contains( key );
@ -137,7 +138,7 @@ public class CacheImpl implements CacheImplementor {
@Override @Override
public void evictEntity(String entityName, Serializable identifier) { public void evictEntity(String entityName, Serializable identifier) {
EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName ); EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName );
if ( p.hasCache() ) { if ( p.canWriteToCache() ) {
if ( LOG.isDebugEnabled() ) { if ( LOG.isDebugEnabled() ) {
LOG.debugf( LOG.debugf(
"Evicting second-level cache: %s", "Evicting second-level cache: %s",
@ -158,7 +159,7 @@ public class CacheImpl implements CacheImplementor {
@Override @Override
public void evictEntityRegion(String entityName) { public void evictEntityRegion(String entityName) {
EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName ); EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName );
if ( p.hasCache() ) { if ( p.canWriteToCache() ) {
if ( LOG.isDebugEnabled() ) { if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Evicting second-level cache: %s", p.getEntityName() ); LOG.debugf( "Evicting second-level cache: %s", p.getEntityName() );
} }
@ -428,12 +429,23 @@ public class CacheImpl implements CacheImplementor {
throw new PersistenceException( "Hibernate cannot unwrap Cache as " + cls.getName() ); throw new PersistenceException( "Hibernate cannot unwrap Cache as " + cls.getName() );
} }
// todo (5.3) : normalize caching to the "first subclass" in the hierarchy whose subclasses all define caching
// 5.3 adds support for "subclass only" caching, so we need a different paradigm for
// code such as this that assumes root-only caching
@Override @Override
public EntityRegionAccessStrategy determineEntityRegionAccessStrategy(PersistentClass model) { public EntityRegionAccessStrategy determineEntityRegionAccessStrategy(PersistentClass model) {
final String cacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName(); if ( ! settings.isSecondLevelCacheEnabled() ) {
return null;
}
// cache settings are defined on root entity
final RootClass rootEntity = model.getRootClass();
final String cacheRegionName = cacheRegionPrefix + rootEntity.getCacheRegionName();
EntityRegionAccessStrategy accessStrategy = entityRegionAccessStrategyMap.get( cacheRegionName ); EntityRegionAccessStrategy accessStrategy = entityRegionAccessStrategyMap.get( cacheRegionName );
if ( accessStrategy == null && settings.isSecondLevelCacheEnabled() ) { if ( accessStrategy == null ) {
final AccessType accessType = AccessType.fromExternalName( model.getCacheConcurrencyStrategy() ); final AccessType accessType = AccessType.fromExternalName( rootEntity.getCacheConcurrencyStrategy() );
if ( accessType != null ) { if ( accessType != null ) {
LOG.tracef( "Building shared cache region for entity data [%s]", model.getEntityName() ); LOG.tracef( "Building shared cache region for entity data [%s]", model.getEntityName() );
EntityRegion entityRegion = regionFactory.buildEntityRegion( EntityRegion entityRegion = regionFactory.buildEntityRegion(

View File

@ -1768,7 +1768,7 @@ public interface CoreMessageLogger extends BasicLogger {
void unknownJavaTypeNoEqualsHashCode(Class javaType); void unknownJavaTypeNoEqualsHashCode(Class javaType);
@LogMessage(level = WARN) @LogMessage(level = WARN)
@Message(value = "@javax.persistence.Cacheable or @org.hibernate.annotations.Cache used on a non-root entity: ignored for %s", id = 482) @Message(value = "@org.hibernate.annotations.Cache used on a non-root entity: ignored for %s", id = 482)
void cacheOrCacheableAnnotationOnNonRoot(String className); void cacheOrCacheableAnnotationOnNonRoot(String className);
@LogMessage(level = WARN) @LogMessage(level = WARN)

View File

@ -219,7 +219,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
// ); // );
// } // }
if ( persister.hasCache() ) { if ( persister.canWriteToCache() ) {
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey( id, persister, getFactory(), getTenantIdentifier() ); final Object ck = cache.generateCacheKey( id, persister, getFactory(), getTenantIdentifier() );
cache.evict( ck ); cache.evict( ck );

View File

@ -90,6 +90,8 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
private Component declaredIdentifierMapper; private Component declaredIdentifierMapper;
private OptimisticLockStyle optimisticLockStyle; private OptimisticLockStyle optimisticLockStyle;
private boolean isCached;
public PersistentClass(MetadataBuildingContext metadataBuildingContext) { public PersistentClass(MetadataBuildingContext metadataBuildingContext) {
this.metadataBuildingContext = metadataBuildingContext; this.metadataBuildingContext = metadataBuildingContext;
} }
@ -270,10 +272,35 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
public abstract boolean isVersioned(); public abstract boolean isVersioned();
public abstract String getNaturalIdCacheRegionName();
public boolean isCached() {
return isCached;
}
public void setCached(boolean cached) {
isCached = cached;
}
/**
* Use {@link #isCached} instead
*/
@Deprecated
public boolean isCachingExplicitlyRequested() {
return isCached();
}
/**
* Use {@link #setCached} instead
*/
@Deprecated
public void setCachingExplicitlyRequested(boolean cached) {
setCached( cached );
}
public abstract String getCacheConcurrencyStrategy(); public abstract String getCacheConcurrencyStrategy();
public abstract String getNaturalIdCacheRegionName();
public abstract PersistentClass getSuperclass(); public abstract PersistentClass getSuperclass();
public abstract boolean isExplicitPolymorphism(); public abstract boolean isExplicitPolymorphism();
@ -955,8 +982,6 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
return false; return false;
} }
public abstract boolean isLazyPropertiesCacheable();
// The following methods are added to support @MappedSuperclass in the metamodel // The following methods are added to support @MappedSuperclass in the metamodel
public Iterator getDeclaredPropertyIterator() { public Iterator getDeclaredPropertyIterator() {
ArrayList iterators = new ArrayList(); ArrayList iterators = new ArrayList();

View File

@ -34,10 +34,12 @@ public class RootClass extends PersistentClass implements TableOwner {
private KeyValue identifier; private KeyValue identifier;
private Property version; private Property version;
private boolean polymorphic; private boolean polymorphic;
private String cacheConcurrencyStrategy; private String cacheConcurrencyStrategy;
private String cacheRegionName; private String cacheRegionName;
private String naturalIdCacheRegionName;
private boolean lazyPropertiesCacheable = true; private boolean lazyPropertiesCacheable = true;
private String naturalIdCacheRegionName;
private Value discriminator; private Value discriminator;
private boolean mutable = true; private boolean mutable = true;
private boolean embeddedIdentifier; private boolean embeddedIdentifier;
@ -50,7 +52,6 @@ public class RootClass extends PersistentClass implements TableOwner {
private int nextSubclassId; private int nextSubclassId;
private Property declaredIdentifierProperty; private Property declaredIdentifierProperty;
private Property declaredVersion; private Property declaredVersion;
private boolean cachingExplicitlyRequested;
public RootClass(MetadataBuildingContext metadataBuildingContext) { public RootClass(MetadataBuildingContext metadataBuildingContext) {
super( metadataBuildingContext ); super( metadataBuildingContext );
@ -314,6 +315,14 @@ public class RootClass extends PersistentClass implements TableOwner {
this.cacheRegionName = cacheRegionName; this.cacheRegionName = cacheRegionName;
} }
public boolean isLazyPropertiesCacheable() {
return lazyPropertiesCacheable;
}
public void setLazyPropertiesCacheable(boolean lazyPropertiesCacheable) {
this.lazyPropertiesCacheable = lazyPropertiesCacheable;
}
@Override @Override
public String getNaturalIdCacheRegionName() { public String getNaturalIdCacheRegionName() {
return naturalIdCacheRegionName; return naturalIdCacheRegionName;
@ -323,15 +332,6 @@ public class RootClass extends PersistentClass implements TableOwner {
this.naturalIdCacheRegionName = naturalIdCacheRegionName; this.naturalIdCacheRegionName = naturalIdCacheRegionName;
} }
@Override
public boolean isLazyPropertiesCacheable() {
return lazyPropertiesCacheable;
}
public void setLazyPropertiesCacheable(boolean lazyPropertiesCacheable) {
this.lazyPropertiesCacheable = lazyPropertiesCacheable;
}
@Override @Override
public boolean isJoinedSubclass() { public boolean isJoinedSubclass() {
return false; return false;
@ -360,11 +360,4 @@ public class RootClass extends PersistentClass implements TableOwner {
return mv.accept( this ); return mv.accept( this );
} }
public void setCachingExplicitlyRequested(boolean explicitlyRequested) {
this.cachingExplicitlyRequested = explicitlyRequested;
}
public boolean isCachingExplicitlyRequested() {
return cachingExplicitlyRequested;
}
} }

View File

@ -47,7 +47,7 @@ public class Subclass extends PersistentClass {
} }
public String getCacheConcurrencyStrategy() { public String getCacheConcurrencyStrategy() {
return getSuperclass().getCacheConcurrencyStrategy(); return getRootClass().getCacheConcurrencyStrategy();
} }
public RootClass getRootClass() { public RootClass getRootClass() {
@ -190,10 +190,6 @@ public class Subclass extends PersistentClass {
this.classPersisterClass = classPersisterClass; this.classPersisterClass = classPersisterClass;
} }
public boolean isLazyPropertiesCacheable() {
return getSuperclass().isLazyPropertiesCacheable();
}
public int getJoinClosureSpan() { public int getJoinClosureSpan() {
return getSuperclass().getJoinClosureSpan() + super.getJoinClosureSpan(); return getSuperclass().getJoinClosureSpan() + super.getJoinClosureSpan();
} }

View File

@ -97,6 +97,7 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
@ -148,6 +149,8 @@ public abstract class AbstractEntityPersister
// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final SessionFactoryImplementor factory; private final SessionFactoryImplementor factory;
private final boolean canReadFromCache;
private final boolean canWriteToCache;
private final EntityRegionAccessStrategy cacheAccessStrategy; private final EntityRegionAccessStrategy cacheAccessStrategy;
private final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy; private final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy;
private final boolean isLazyPropertiesCacheable; private final boolean isLazyPropertiesCacheable;
@ -512,9 +515,21 @@ public abstract class AbstractEntityPersister
// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.factory = creationContext.getSessionFactory(); this.factory = creationContext.getSessionFactory();
this.cacheAccessStrategy = cacheAccessStrategy;
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy; if ( creationContext.getSessionFactory().getSessionFactoryOptions().isSecondLevelCacheEnabled() ) {
isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable(); this.canWriteToCache = persistentClass.isCached();
this.canReadFromCache = determineCanReadFromCache( persistentClass );
this.cacheAccessStrategy = cacheAccessStrategy;
this.isLazyPropertiesCacheable = persistentClass.getRootClass().isLazyPropertiesCacheable();
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy;
}
else {
this.canWriteToCache = false;
this.canReadFromCache = false;
this.cacheAccessStrategy = null;
this.isLazyPropertiesCacheable = true;
this.naturalIdRegionAccessStrategy = null;
}
this.entityMetamodel = new EntityMetamodel( persistentClass, this, factory ); this.entityMetamodel = new EntityMetamodel( persistentClass, this, factory );
this.entityTuplizer = this.entityMetamodel.getTuplizer(); this.entityTuplizer = this.entityMetamodel.getTuplizer();
@ -830,6 +845,22 @@ public abstract class AbstractEntityPersister
} }
@SuppressWarnings("unchecked")
private boolean determineCanReadFromCache(PersistentClass persistentClass) {
if ( persistentClass.isCached() ) {
return true;
}
final Iterator<Subclass> subclassIterator = persistentClass.getSubclassIterator();
while ( subclassIterator.hasNext() ) {
final Subclass subclass = subclassIterator.next();
if ( subclass.isCached() ) {
return true;
}
}
return false;
}
protected CacheEntryHelper buildCacheEntryHelper() { protected CacheEntryHelper buildCacheEntryHelper() {
if ( cacheAccessStrategy == null ) { if ( cacheAccessStrategy == null ) {
// the entity defined no caching... // the entity defined no caching...
@ -985,7 +1016,7 @@ public abstract class AbstractEntityPersister
); );
} }
if ( session.getCacheMode().isGetEnabled() && hasCache() && isLazyPropertiesCacheable() ) { if ( session.getCacheMode().isGetEnabled() && canReadFromCache() && isLazyPropertiesCacheable() ) {
final EntityRegionAccessStrategy cache = getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = getCacheAccessStrategy();
final Object cacheKey = cache.generateCacheKey(id, this, session.getFactory(), session.getTenantIdentifier() ); final Object cacheKey = cache.generateCacheKey(id, this, session.getFactory(), session.getTenantIdentifier() );
final Object ce = CacheHelper.fromSharedCache( session, cacheKey, cache ); final Object ce = CacheHelper.fromSharedCache( session, cacheKey, cache );
@ -4293,8 +4324,18 @@ public abstract class AbstractEntityPersister
return entityMetamodel; return entityMetamodel;
} }
@Override
public boolean canReadFromCache() {
return canReadFromCache;
}
@Override
public boolean canWriteToCache() {
return canWriteToCache;
}
public boolean hasCache() { public boolean hasCache() {
return cacheAccessStrategy != null; return canWriteToCache;
} }
public EntityRegionAccessStrategy getCacheAccessStrategy() { public EntityRegionAccessStrategy getCacheAccessStrategy() {
@ -4467,7 +4508,7 @@ public abstract class AbstractEntityPersister
} }
// check to see if it is in the second-level cache // check to see if it is in the second-level cache
if ( session.getCacheMode().isGetEnabled() && hasCache() ) { if ( session.getCacheMode().isGetEnabled() && canReadFromCache() ) {
final EntityRegionAccessStrategy cache = getCacheAccessStrategy(); final EntityRegionAccessStrategy cache = getCacheAccessStrategy();
final Object ck = cache.generateCacheKey( id, this, session.getFactory(), session.getTenantIdentifier() ); final Object ck = cache.generateCacheKey( id, this, session.getFactory(), session.getTenantIdentifier() );
final Object ce = CacheHelper.fromSharedCache( session, ck, getCacheAccessStrategy() ); final Object ce = CacheHelper.fromSharedCache( session, ck, getCacheAccessStrategy() );

View File

@ -503,9 +503,16 @@ public interface EntityPersister extends OptimisticCacheSource, EntityDefinition
* Should lazy properties of this entity be cached? * Should lazy properties of this entity be cached?
*/ */
boolean isLazyPropertiesCacheable(); boolean isLazyPropertiesCacheable();
boolean canReadFromCache();
boolean canWriteToCache();
/** /**
* Does this class have a cache. * Does this class have a cache.
*
* @deprecated Use {@link #canReadFromCache()} and/or {@link #canWriteToCache()} depending on need
*/ */
@Deprecated
boolean hasCache(); boolean hasCache();
/** /**
* Get the cache (optional operation) * Get the cache (optional operation)

View File

@ -13,7 +13,6 @@ import javax.persistence.EntityManagerFactory;
import javax.persistence.SharedCacheMode; import javax.persistence.SharedCacheMode;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cache.internal.NoCachingRegionFactory;
import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.AvailableSettings;
@ -24,12 +23,11 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.testing.cache.CachingRegionFactory; import org.hibernate.testing.cache.CachingRegionFactory;
import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue;
/** /**
* this is hacky transient step until EMF building is integrated with metamodel * this is hacky transient step until EMF building is integrated with metamodel
@ -51,13 +49,13 @@ public class ConfigurationTest extends BaseUnitTestCase {
MetadataImplementor metadata = buildMetadata( SharedCacheMode.NONE ); MetadataImplementor metadata = buildMetadata( SharedCacheMode.NONE );
PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
} }
@Test @Test
@ -65,13 +63,13 @@ public class ConfigurationTest extends BaseUnitTestCase {
MetadataImplementor metadata = buildMetadata( SharedCacheMode.UNSPECIFIED ); MetadataImplementor metadata = buildMetadata( SharedCacheMode.UNSPECIFIED );
PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
} }
@Test @Test
@ -79,13 +77,13 @@ public class ConfigurationTest extends BaseUnitTestCase {
MetadataImplementor metadata = buildMetadata( SharedCacheMode.ALL ); MetadataImplementor metadata = buildMetadata( SharedCacheMode.ALL );
PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() );
assertNotNull( pc.getCacheConcurrencyStrategy() ); assertTrue( pc.isCached() );
pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() );
assertNotNull( pc.getCacheConcurrencyStrategy() ); assertTrue( pc.isCached() );
pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() );
assertNotNull( pc.getCacheConcurrencyStrategy() ); assertTrue( pc.isCached() );
} }
@Test @Test
@ -93,13 +91,13 @@ public class ConfigurationTest extends BaseUnitTestCase {
MetadataImplementor metadata = buildMetadata( SharedCacheMode.ENABLE_SELECTIVE ); MetadataImplementor metadata = buildMetadata( SharedCacheMode.ENABLE_SELECTIVE );
PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() );
assertNotNull( pc.getCacheConcurrencyStrategy() ); assertTrue( pc.isCached() );
pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
} }
@Test @Test
@ -107,13 +105,13 @@ public class ConfigurationTest extends BaseUnitTestCase {
MetadataImplementor metadata = buildMetadata( SharedCacheMode.DISABLE_SELECTIVE ); MetadataImplementor metadata = buildMetadata( SharedCacheMode.DISABLE_SELECTIVE );
PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() );
assertNotNull( pc.getCacheConcurrencyStrategy() ); assertTrue( pc.isCached() );
pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() );
assertNull( pc.getCacheConcurrencyStrategy() ); assertFalse( pc.isCached() );
pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() );
assertNotNull( pc.getCacheConcurrencyStrategy() ); assertTrue( pc.isCached() );
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -411,6 +411,16 @@ public class PersisterClassProviderTest {
return false; return false;
} }
@Override
public boolean canReadFromCache() {
return false;
}
@Override
public boolean canWriteToCache() {
return false;
}
@Override @Override
public boolean hasCache() { public boolean hasCache() {
return false; return false;

View File

@ -33,6 +33,7 @@ import org.junit.Test;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -65,7 +66,7 @@ public class NonRootEntityWithCacheAnnotationTest {
.buildMetadata(); .buildMetadata();
assertTrue( triggerable.wasTriggered() ); assertTrue( triggerable.wasTriggered() );
assertNull( ( metadata.getEntityBinding( AEntity.class.getName() ) ).getCacheConcurrencyStrategy() ); assertFalse( metadata.getEntityBinding( AEntity.class.getName() ).isCached() );
serviceRegistry.destroy(); serviceRegistry.destroy();
} }

View File

@ -32,7 +32,7 @@ import org.junit.Test;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
@ -64,8 +64,10 @@ public class NonRootEntityWithCacheableAnnotationTest {
.addAnnotatedClass( AEntity.class ) .addAnnotatedClass( AEntity.class )
.buildMetadata(); .buildMetadata();
assertTrue( triggerable.wasTriggered() ); assertFalse( metadata.getEntityBinding( ABase.class.getName() ).isCached() );
assertNull( ( metadata.getEntityBinding( AEntity.class.getName() ) ).getCacheConcurrencyStrategy() ); assertTrue( metadata.getEntityBinding( AEntity.class.getName() ).isCached() );
assertFalse( triggerable.wasTriggered() );
serviceRegistry.destroy(); serviceRegistry.destroy();
} }

View File

@ -46,7 +46,7 @@ public class PolymorphicCacheTest extends BaseCoreFunctionalTestCase {
s.beginTransaction(); s.beginTransaction();
// See HHH-9107 // See HHH-9107
try { try {
s.get( CachedItem2.class, item1.getId() ); final CachedItem2 tmp = s.get( CachedItem2.class, item1.getId() );
fail( "Expected a WrongClassException to be thrown." ); fail( "Expected a WrongClassException to be thrown." );
} }
catch (WrongClassException e) { catch (WrongClassException e) {

View File

@ -28,7 +28,9 @@ import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/** /**
* @author Gail Badner * @author Gail Badner
@ -60,6 +62,8 @@ public class DefaultCacheConcurrencyPropertyTest extends BaseUnitTestCase {
final SessionFactoryImplementor sf = (SessionFactoryImplementor) metadata.buildSessionFactory(); final SessionFactoryImplementor sf = (SessionFactoryImplementor) metadata.buildSessionFactory();
try { try {
final EntityPersister persister = sf.getMetamodel().entityPersister( TheEntity.class.getName() ); final EntityPersister persister = sf.getMetamodel().entityPersister( TheEntity.class.getName() );
assertTrue( persister.canReadFromCache() );
assertTrue( persister.canWriteToCache() );
assertNotNull( persister.getCacheAccessStrategy() ); assertNotNull( persister.getCacheAccessStrategy() );
} }
finally { finally {

View File

@ -384,6 +384,16 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
return false; return false;
} }
@Override
public boolean canReadFromCache() {
return false;
}
@Override
public boolean canWriteToCache() {
return false;
}
@Override @Override
public boolean hasCache() { public boolean hasCache() {
return false; return false;

View File

@ -6,22 +6,89 @@
*/ */
package org.hibernate.test.jpa.compliance.tck2_2; package org.hibernate.test.jpa.compliance.tck2_2;
import java.util.Map;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Inheritance; import javax.persistence.Inheritance;
import javax.persistence.InheritanceType; import javax.persistence.InheritanceType;
import javax.persistence.SharedCacheMode;
import javax.persistence.Table; import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources; import org.hibernate.boot.MetadataSources;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.hamcrest.CoreMatchers;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class SubclassOnlyCachingTests extends BaseNonConfigCoreFunctionalTestCase { public class SubclassOnlyCachingTests extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testMapping() {
assertThat(
sessionFactory().getMetamodel().entityPersister( Person.class ).hasCache(),
CoreMatchers.is( false )
);
assertThat(
sessionFactory().getMetamodel().entityPersister( Employee.class ).hasCache(),
CoreMatchers.is( false )
);
assertThat(
sessionFactory().getMetamodel().entityPersister( Customer.class ).hasCache(),
CoreMatchers.is( true )
);
}
@Test
public void testOnlySubclassIsCached() {
final StatisticsImplementor statistics = sessionFactory().getStatistics();
inTransaction(
s -> s.persist( new Customer( 1, "Acme Corp", "123" ) )
);
assertTrue( sessionFactory().getCache().contains( Customer.class, 1 ) );
inTransaction(
s -> {
statistics.clear();
final Customer customer = s.get( Customer.class, 1 );
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( statistics.getSecondLevelCacheHitCount(), CoreMatchers.is(1L) );
}
);
}
@After
public void cleanupData() {
inTransaction(
s -> s.createQuery( "delete from Customer" ).executeUpdate()
);
}
@Override
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, "true" );
settings.put( AvailableSettings.GENERATE_STATISTICS, "true" );
settings.put( AvailableSettings.JPA_SHARED_CACHE_MODE, SharedCacheMode.ENABLE_SELECTIVE );
}
@Override @Override
protected void applyMetadataSources(MetadataSources sources) { protected void applyMetadataSources(MetadataSources sources) {
@ -31,11 +98,6 @@ public class SubclassOnlyCachingTests extends BaseNonConfigCoreFunctionalTestCas
sources.addAnnotatedClass( Customer.class ); sources.addAnnotatedClass( Customer.class );
} }
@Test
public void testOnlySubclassIsCached() {
}
@Entity( name = "Person" ) @Entity( name = "Person" )
@Table( name = "persons" ) @Table( name = "persons" )
@Inheritance( strategy = InheritanceType.SINGLE_TABLE ) @Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@ -43,18 +105,43 @@ public class SubclassOnlyCachingTests extends BaseNonConfigCoreFunctionalTestCas
@Id @Id
public Integer id; public Integer id;
public String name; public String name;
public Person() {
}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
} }
@Entity @Entity( name = "Employee" )
public static class Employee extends Person { public static class Employee extends Person {
public String employeeCode; public String employeeCode;
public String costCenter; public String costCenter;
public Employee() {
}
public Employee(Integer id, String name, String employeeCode, String costCenter) {
super( id, name );
this.employeeCode = employeeCode;
this.costCenter = costCenter;
}
} }
@Entity @Entity( name = "Customer" )
@Cacheable() @Cacheable()
public static class Customer extends Person { public static class Customer extends Person {
public String erpCode; public String erpCode;
public Customer() {
}
public Customer(Integer id, String name, String erpCode) {
super( id, name );
this.erpCode = erpCode;
}
} }
} }

View File

@ -653,6 +653,16 @@ public class CustomPersister implements EntityPersister {
return true; return true;
} }
@Override
public boolean canReadFromCache() {
return false;
}
@Override
public boolean canWriteToCache() {
return false;
}
@Override @Override
public boolean isVersionPropertyGenerated() { public boolean isVersionPropertyGenerated() {
return false; return false;

View File

@ -21,6 +21,7 @@ import org.hibernate.cfg.Environment;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass; import org.hibernate.mapping.RootClass;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool; import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
@ -70,7 +71,9 @@ public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase {
ms.addAnnotatedClass(Customer.class); ms.addAnnotatedClass(Customer.class);
Metadata metadata = ms.buildMetadata(); Metadata metadata = ms.buildMetadata();
((RootClass) metadata.getEntityBinding(Customer.class.getName())).setCacheConcurrencyStrategy("read-write"); final PersistentClass customerMapping = metadata.getEntityBinding( Customer.class.getName() );
customerMapping.setCached( true );
((RootClass) customerMapping ).setCacheConcurrencyStrategy( "read-write");
HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool(); HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool();
tool.injectServices(serviceRegistry); tool.injectServices(serviceRegistry);

View File

@ -19,6 +19,7 @@ import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass; import org.hibernate.mapping.RootClass;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.Stoppable; import org.hibernate.service.spi.Stoppable;
@ -74,7 +75,9 @@ public abstract class AbstractSchemaBasedMultiTenancyTest<T extends MultiTenantC
ms.addAnnotatedClass( Invoice.class ); ms.addAnnotatedClass( Invoice.class );
Metadata metadata = ms.buildMetadata(); Metadata metadata = ms.buildMetadata();
( (RootClass) metadata.getEntityBinding( Customer.class.getName() ) ).setCacheConcurrencyStrategy( "read-write" ); final PersistentClass customerMapping = metadata.getEntityBinding( Customer.class.getName() );
customerMapping.setCached( true );
( (RootClass) customerMapping ).setCacheConcurrencyStrategy( "read-write" );
HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool(); HibernateSchemaManagementTool tool = new HibernateSchemaManagementTool();
tool.injectServices( serviceRegistry ); tool.injectServices( serviceRegistry );

View File

@ -344,6 +344,7 @@ public class BaseNonConfigCoreFunctionalTestCase extends BaseUnitTestCase {
if ( !hasLob ) { if ( !hasLob ) {
( ( RootClass) entityBinding ).setCacheConcurrencyStrategy( getCacheConcurrencyStrategy() ); ( ( RootClass) entityBinding ).setCacheConcurrencyStrategy( getCacheConcurrencyStrategy() );
entityBinding.setCached( true );
} }
} }