From 59c3baae3271247bed516fe50952b65be1a27e5b Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 12 Dec 2017 21:22:43 -0600 Subject: [PATCH] HHH-12146 - Support enabling caching at any level within a mapped hierarchy --- .../internal/BulkOperationCleanupAction.java | 4 +- .../action/internal/EntityAction.java | 2 +- .../action/internal/EntityDeleteAction.java | 6 +- .../action/internal/EntityInsertAction.java | 2 +- .../action/internal/EntityUpdateAction.java | 6 +- .../InFlightMetadataCollectorImpl.java | 1 + .../source/internal/hbm/ModelBinder.java | 2 +- .../org/hibernate/cfg/AnnotationBinder.java | 80 +----- .../cfg/annotations/EntityBinder.java | 232 ++++++++++++++---- .../internal/StatefulPersistenceContext.java | 4 +- .../engine/internal/TwoPhaseLoad.java | 2 +- .../hibernate/engine/spi/BatchFetchQueue.java | 2 +- .../AbstractLockUpgradeEventListener.java | 2 +- .../internal/DefaultLoadEventListener.java | 6 +- .../internal/DefaultRefreshEventListener.java | 2 +- .../hql/internal/ast/tree/FromElement.java | 9 - .../org/hibernate/internal/CacheImpl.java | 24 +- .../hibernate/internal/CoreMessageLogger.java | 2 +- .../internal/StatelessSessionImpl.java | 2 +- .../hibernate/mapping/PersistentClass.java | 31 ++- .../java/org/hibernate/mapping/RootClass.java | 29 +-- .../java/org/hibernate/mapping/Subclass.java | 6 +- .../entity/AbstractEntityPersister.java | 53 +++- .../persister/entity/EntityPersister.java | 7 + .../annotation/ConfigurationTest.java | 36 ++- .../PersisterClassProviderTest.java | 10 + .../NonRootEntityWithCacheAnnotationTest.java | 3 +- ...RootEntityWithCacheableAnnotationTest.java | 8 +- .../polymorphism/PolymorphicCacheTest.java | 2 +- .../DefaultCacheConcurrencyPropertyTest.java | 4 + .../GoofyPersisterClassProvider.java | 10 + .../tck2_2/SubclassOnlyCachingTests.java | 101 +++++++- .../test/legacy/CustomPersister.java | 10 + .../DiscriminatorMultiTenancyTest.java | 5 +- .../AbstractSchemaBasedMultiTenancyTest.java | 5 +- .../BaseNonConfigCoreFunctionalTestCase.java | 1 + 36 files changed, 483 insertions(+), 228 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java index 103137a70e..b0c296fb32 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java @@ -61,7 +61,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable { for ( Queryable persister : affectedQueryables ) { spacesList.addAll( Arrays.asList( (String[]) persister.getQuerySpaces() ) ); - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { entityCleanups.add( new EntityCleanup( persister.getCacheAccessStrategy() ) ); } if ( persister.hasNaturalIdentifier() && persister.hasNaturalIdCache() ) { @@ -105,7 +105,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable { if ( affectedEntity( tableSpaces, entitySpaces ) ) { spacesList.addAll( Arrays.asList( entitySpaces ) ); - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { entityCleanups.add( new EntityCleanup( persister.getCacheAccessStrategy() ) ); } if ( persister.hasNaturalIdentifier() && persister.hasNaturalIdCache() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityAction.java index 0a62446618..93e079dfbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityAction.java @@ -78,7 +78,7 @@ public abstract class EntityAction protected abstract boolean hasPostCommitEventListeners(); protected boolean needsAfterTransactionCompletion() { - return persister.hasCache() || hasPostCommitEventListeners(); + return persister.canWriteToCache() || hasPostCommitEventListeners(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java index dd2ff38ab8..66450e12d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java @@ -86,7 +86,7 @@ public class EntityDeleteAction extends EntityAction { } final Object ck; - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); ck = cache.generateCacheKey( id, persister, session.getFactory(), session.getTenantIdentifier() ); lock = cache.lockItem( session, ck, version ); @@ -113,7 +113,7 @@ public class EntityDeleteAction extends EntityAction { persistenceContext.removeEntity( entry.getEntityKey() ); persistenceContext.removeProxy( entry.getEntityKey() ); - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { persister.getCacheAccessStrategy().remove( session, ck); } @@ -187,7 +187,7 @@ public class EntityDeleteAction extends EntityAction { @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws HibernateException { EntityPersister entityPersister = getPersister(); - if ( entityPersister.hasCache() ) { + if ( entityPersister.canWriteToCache() ) { EntityRegionAccessStrategy cache = entityPersister.getCacheAccessStrategy(); final Object ck = cache.generateCacheKey( getId(), diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java index 2eaf7105ea..1aa7d11b4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java @@ -249,7 +249,7 @@ public final class EntityInsertAction extends AbstractEntityInsertAction { } private boolean isCachePutEnabled(EntityPersister persister, SharedSessionContractImplementor session) { - return persister.hasCache() + return persister.canWriteToCache() && !persister.isCacheInvalidationRequired() && session.getCacheMode().isPutEnabled(); } diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java index 54893394ad..f8d5483be2 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java @@ -127,7 +127,7 @@ public final class EntityUpdateAction extends EntityAction { } final Object ck; - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); ck = cache.generateCacheKey( id, @@ -184,7 +184,7 @@ public final class EntityUpdateAction extends EntityAction { entry.postUpdate( instance, state, nextVersion ); } - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { if ( persister.isCacheInvalidationRequired() || entry.getStatus()!= Status.MANAGED ) { persister.getCacheAccessStrategy().remove( session, ck); } @@ -310,7 +310,7 @@ public final class EntityUpdateAction extends EntityAction { @Override public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws CacheException { final EntityPersister persister = getPersister(); - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final Object ck = cache.generateCacheKey( getId(), diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java index 835e84a7d3..9dc06bfaab 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java @@ -2146,6 +2146,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector "Cache override referenced a non-root entity : " + cacheRegionDefinition.getRole() ); } + entityBinding.setCached( true ); ( (RootClass) entityBinding ).setCacheRegionName( cacheRegionDefinition.getRegion() ); ( (RootClass) entityBinding ).setCacheConcurrencyStrategy( cacheRegionDefinition.getUsage() ); ( (RootClass) entityBinding ).setLazyPropertiesCacheable( cacheRegionDefinition.isCacheLazy() ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index ee45e01fc2..38e79eaa3e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -331,7 +331,7 @@ public class ModelBinder { } rootEntityDescriptor.setCacheRegionName( caching.getRegion() ); rootEntityDescriptor.setLazyPropertiesCacheable( caching.isCacheLazyProperties() ); - rootEntityDescriptor.setCachingExplicitlyRequested( caching.getRequested() != TruthValue.UNKNOWN ); + rootEntityDescriptor.setCached( caching.getRequested() != TruthValue.UNKNOWN ); } private void bindEntityIdentifier( diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 767d5287e3..620eecc75a 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -6,7 +6,6 @@ */ package org.hibernate.cfg; -import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -21,7 +20,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; import javax.persistence.Basic; -import javax.persistence.Cacheable; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ConstraintMode; @@ -78,7 +76,6 @@ import org.hibernate.FetchMode; import org.hibernate.MappingException; import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.Check; @@ -105,7 +102,6 @@ import org.hibernate.annotations.ListIndexBase; import org.hibernate.annotations.ManyToAny; import org.hibernate.annotations.MapKeyType; import org.hibernate.annotations.NaturalId; -import org.hibernate.annotations.NaturalIdCache; import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.OnDelete; @@ -625,8 +621,7 @@ public final class AnnotationBinder { entityBinder.setProxy( clazzToProcess.getAnnotation( Proxy.class ) ); entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) ); entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) ); - entityBinder.setCache( determineCacheSettings( clazzToProcess, context ) ); - entityBinder.setNaturalIdCache( clazzToProcess, clazzToProcess.getAnnotation( NaturalIdCache.class ) ); + applyCacheSettings( entityBinder, clazzToProcess, context ); bindFilters( clazzToProcess, entityBinder, context ); @@ -1158,80 +1153,19 @@ public final class AnnotationBinder { } } - private static Cache determineCacheSettings(XClass clazzToProcess, MetadataBuildingContext context) { - Cache cacheAnn = clazzToProcess.getAnnotation( Cache.class ); - if ( cacheAnn != null ) { - return cacheAnn; - } + private static void applyCacheSettings(EntityBinder binder, XClass clazzToProcess, MetadataBuildingContext context) { + binder.applyCaching( + clazzToProcess, + 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) { 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 annotationType() { - return Cache.class; - } - } - private static PersistentClass makePersistentClass( InheritanceState inheritanceState, PersistentClass superEntity, diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java index 4766ddad3b..51262e13d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java @@ -6,6 +6,7 @@ */ package org.hibernate.cfg.annotations; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -15,7 +16,6 @@ import javax.persistence.Access; import javax.persistence.Cacheable; import javax.persistence.ConstraintMode; import javax.persistence.Entity; -import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.NamedEntityGraph; @@ -23,6 +23,7 @@ import javax.persistence.NamedEntityGraphs; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.SecondaryTable; import javax.persistence.SecondaryTables; +import javax.persistence.SharedCacheMode; import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; @@ -127,17 +128,19 @@ public class EntityBinder { // atm we use both from here; HBM binding solely uses InFlightMetadataCollector.EntityTableXref private java.util.Map secondaryTables = new HashMap(); private java.util.Map secondaryTableJoins = new HashMap(); - private String cacheConcurrentStrategy; - private String cacheRegion; - private String naturalIdCacheRegion; private List filters = new ArrayList(); private InheritanceState inheritanceState; private boolean ignoreIdAnnotations; - private boolean cacheLazyProperty; private AccessType propertyAccessType = AccessType.DEFAULT; private boolean wrapIdsInEmbeddedComponents; private String subselect; + private boolean isCached; + private String cacheConcurrentStrategy; + private String cacheRegion; + private boolean cacheLazyProperty; + private String naturalIdCacheRegion; + public boolean wrapIdsInEmbeddedComponents() { return wrapIdsInEmbeddedComponents; } @@ -281,18 +284,26 @@ public class EntityBinder { } rootClass.setMutable( mutable ); rootClass.setExplicitPolymorphism( isExplicitPolymorphism( polymorphismType ) ); - if ( StringHelper.isNotEmpty( where ) ) rootClass.setWhere( where ); + + if ( StringHelper.isNotEmpty( where ) ) { + rootClass.setWhere( where ); + } + if ( cacheConcurrentStrategy != null ) { rootClass.setCacheConcurrencyStrategy( cacheConcurrentStrategy ); rootClass.setCacheRegionName( cacheRegion ); rootClass.setLazyPropertiesCacheable( cacheLazyProperty ); } + rootClass.setNaturalIdCacheRegionName( naturalIdCacheRegion ); + boolean forceDiscriminatorInSelects = forceDiscriminator == null ? context.getBuildingOptions().shouldImplicitlyForceDiscriminatorInSelect() : forceDiscriminator; + rootClass.setForceDiscriminator( forceDiscriminatorInSelects ); - if( insertableDiscriminator != null) { + + if ( insertableDiscriminator != null ) { rootClass.setDiscriminatorInsertable( insertableDiscriminator ); } } @@ -303,11 +314,10 @@ public class EntityBinder { if (annotatedClass.isAnnotationPresent(Immutable.class)) { 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.setSelectBeforeUpdate( selectBeforeUpdate ); @@ -542,6 +552,164 @@ public class EntityBinder { 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 annotationType() { + return Cache.class; + } + } + + private static CacheConcurrencyStrategy determineCacheConcurrencyStrategy(MetadataBuildingContext context) { + return CacheConcurrencyStrategy.fromAccessType( + context.getBuildingOptions().getImplicitCacheAccessType() + ); + } private static class EntityTableObjectNameSource implements ObjectNameSource { private final String explicitName; @@ -1044,48 +1212,6 @@ public class EntityBinder { 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) { org.hibernate.cache.spi.access.AccessType accessType = strategy.toAccessType(); return accessType == null ? null : accessType.getExternalName(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 285ef74da9..907ddfa11e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -1670,7 +1670,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public void registerInsertedKey(EntityPersister persister, Serializable id) { // we only are worried about registering these if the persister defines caching - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { if ( insertedKeysMap == null ) { insertedKeysMap = new HashMap<>(); } @@ -1687,7 +1687,7 @@ public class StatefulPersistenceContext implements PersistenceContext { @Override public boolean wasInsertedDuringTransaction(EntityPersister persister, Serializable id) { // again, we only really care if the entity is cached - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { if ( insertedKeysMap != null ) { final List insertedEntityIds = insertedKeysMap.get( persister.getRootEntityName() ); if ( insertedEntityIds != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java index fb4166cfb8..b3960e521a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java @@ -183,7 +183,7 @@ public final class TwoPhaseLoad { persister.setPropertyValues( entity, hydratedState ); final SessionFactoryImplementor factory = session.getFactory(); - if ( persister.hasCache() && session.getCacheMode().isPutEnabled() ) { + if ( persister.canWriteToCache() && session.getCacheMode().isPutEnabled() ) { if ( debugEnabled ) { LOG.debugf( diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java index ea636c26b4..1172aceaaa 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java @@ -215,7 +215,7 @@ public class BatchFetchQueue { private boolean isCached(EntityKey entityKey, EntityPersister persister) { 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 Object key = cache.generateCacheKey( entityKey.getIdentifier(), diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractLockUpgradeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractLockUpgradeEventListener.java index 44c197019e..d6c01c05c0 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractLockUpgradeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractLockUpgradeEventListener.java @@ -61,7 +61,7 @@ public abstract class AbstractLockUpgradeEventListener extends AbstractReassocia ); } - final boolean cachingEnabled = persister.hasCache(); + final boolean cachingEnabled = persister.canWriteToCache(); SoftLock lock = null; Object ck = null; try { diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index c9d4e268d6..34c2906e73 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -385,7 +385,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i SoftLock lock = null; final Object ck; final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { ck = cache.generateCacheKey( event.getEntityId(), persister, @@ -403,7 +403,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i entity = load( event, persister, keyToLoad, options ); } finally { - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { cache.unlockItem( source, ck, lock ); } } @@ -583,7 +583,7 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i final EntityKey entityKey) { final SessionImplementor source = event.getSession(); - final boolean useCache = persister.hasCache() + final boolean useCache = persister.canReadFromCache() && source.getCacheMode().isGetEnabled() && event.getLockMode().lessThan( LockMode.READ ); diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index 428ad22516..10373ae4e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -144,7 +144,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener { } } - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { Object previousVersion = null; if ( persister.isVersionPropertyGenerated() ) { // we need to grab the version value from the entity, otherwise diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java index ccc7993d0f..8465de70bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElement.java @@ -629,15 +629,6 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa this.withClauseFragment = withClauseFragment; } - public boolean hasCacheablePersister() { - if ( getQueryableCollection() != null ) { - return getQueryableCollection().hasCache(); - } - else { - return getQueryable().hasCache(); - } - } - public void handlePropertyBeingDereferenced(Type propertySource, String propertyName) { if ( getQueryableCollection() != null && CollectionProperties.isCollectionProperty( propertyName ) ) { // propertyName refers to something like collection.size... diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java index ed32eb9095..c0af7cdc05 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java @@ -38,6 +38,7 @@ import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.Collection; import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.RootClass; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; @@ -119,7 +120,7 @@ public class CacheImpl implements CacheImplementor { @Override public boolean containsEntity(String entityName, Serializable identifier) { EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName ); - if ( p.hasCache() ) { + if ( p.canReadFromCache() ) { EntityRegionAccessStrategy cache = p.getCacheAccessStrategy(); Object key = cache.generateCacheKey( identifier, p, sessionFactory, null ); // have to assume non tenancy return cache.getRegion().contains( key ); @@ -137,7 +138,7 @@ public class CacheImpl implements CacheImplementor { @Override public void evictEntity(String entityName, Serializable identifier) { EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName ); - if ( p.hasCache() ) { + if ( p.canWriteToCache() ) { if ( LOG.isDebugEnabled() ) { LOG.debugf( "Evicting second-level cache: %s", @@ -158,7 +159,7 @@ public class CacheImpl implements CacheImplementor { @Override public void evictEntityRegion(String entityName) { EntityPersister p = sessionFactory.getMetamodel().entityPersister( entityName ); - if ( p.hasCache() ) { + if ( p.canWriteToCache() ) { if ( LOG.isDebugEnabled() ) { 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() ); } + // 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 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 ); - if ( accessStrategy == null && settings.isSecondLevelCacheEnabled() ) { - final AccessType accessType = AccessType.fromExternalName( model.getCacheConcurrencyStrategy() ); + if ( accessStrategy == null ) { + final AccessType accessType = AccessType.fromExternalName( rootEntity.getCacheConcurrencyStrategy() ); if ( accessType != null ) { LOG.tracef( "Building shared cache region for entity data [%s]", model.getEntityName() ); EntityRegion entityRegion = regionFactory.buildEntityRegion( diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index d9b7e29f4e..0c78de70fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1768,7 +1768,7 @@ public interface CoreMessageLogger extends BasicLogger { void unknownJavaTypeNoEqualsHashCode(Class javaType); @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); @LogMessage(level = WARN) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index 873308ba64..d4490fed75 100755 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -219,7 +219,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen // ); // } - if ( persister.hasCache() ) { + if ( persister.canWriteToCache() ) { final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy(); final Object ck = cache.generateCacheKey( id, persister, getFactory(), getTenantIdentifier() ); cache.evict( ck ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index 38dc218108..8b15bdd9ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -90,6 +90,8 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl private Component declaredIdentifierMapper; private OptimisticLockStyle optimisticLockStyle; + private boolean isCached; + public PersistentClass(MetadataBuildingContext metadataBuildingContext) { this.metadataBuildingContext = metadataBuildingContext; } @@ -270,10 +272,35 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl 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 getNaturalIdCacheRegionName(); + public abstract PersistentClass getSuperclass(); public abstract boolean isExplicitPolymorphism(); @@ -955,8 +982,6 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl return false; } - public abstract boolean isLazyPropertiesCacheable(); - // The following methods are added to support @MappedSuperclass in the metamodel public Iterator getDeclaredPropertyIterator() { ArrayList iterators = new ArrayList(); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java index 8f21e41f9f..61f5288df5 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java @@ -34,10 +34,12 @@ public class RootClass extends PersistentClass implements TableOwner { private KeyValue identifier; private Property version; private boolean polymorphic; + private String cacheConcurrencyStrategy; private String cacheRegionName; - private String naturalIdCacheRegionName; private boolean lazyPropertiesCacheable = true; + private String naturalIdCacheRegionName; + private Value discriminator; private boolean mutable = true; private boolean embeddedIdentifier; @@ -50,7 +52,6 @@ public class RootClass extends PersistentClass implements TableOwner { private int nextSubclassId; private Property declaredIdentifierProperty; private Property declaredVersion; - private boolean cachingExplicitlyRequested; public RootClass(MetadataBuildingContext metadataBuildingContext) { super( metadataBuildingContext ); @@ -314,6 +315,14 @@ public class RootClass extends PersistentClass implements TableOwner { this.cacheRegionName = cacheRegionName; } + public boolean isLazyPropertiesCacheable() { + return lazyPropertiesCacheable; + } + + public void setLazyPropertiesCacheable(boolean lazyPropertiesCacheable) { + this.lazyPropertiesCacheable = lazyPropertiesCacheable; + } + @Override public String getNaturalIdCacheRegionName() { return naturalIdCacheRegionName; @@ -323,15 +332,6 @@ public class RootClass extends PersistentClass implements TableOwner { this.naturalIdCacheRegionName = naturalIdCacheRegionName; } - @Override - public boolean isLazyPropertiesCacheable() { - return lazyPropertiesCacheable; - } - - public void setLazyPropertiesCacheable(boolean lazyPropertiesCacheable) { - this.lazyPropertiesCacheable = lazyPropertiesCacheable; - } - @Override public boolean isJoinedSubclass() { return false; @@ -360,11 +360,4 @@ public class RootClass extends PersistentClass implements TableOwner { return mv.accept( this ); } - public void setCachingExplicitlyRequested(boolean explicitlyRequested) { - this.cachingExplicitlyRequested = explicitlyRequested; - } - - public boolean isCachingExplicitlyRequested() { - return cachingExplicitlyRequested; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java b/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java index 89eb399f81..4b367f7908 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java @@ -47,7 +47,7 @@ public class Subclass extends PersistentClass { } public String getCacheConcurrencyStrategy() { - return getSuperclass().getCacheConcurrencyStrategy(); + return getRootClass().getCacheConcurrencyStrategy(); } public RootClass getRootClass() { @@ -190,10 +190,6 @@ public class Subclass extends PersistentClass { this.classPersisterClass = classPersisterClass; } - public boolean isLazyPropertiesCacheable() { - return getSuperclass().isLazyPropertiesCacheable(); - } - public int getJoinClosureSpan() { return getSuperclass().getJoinClosureSpan() + super.getJoinClosureSpan(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index f22687fc72..65a48e2d90 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -97,6 +97,7 @@ import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.Selectable; +import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Table; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.collection.CollectionPersister; @@ -148,6 +149,8 @@ public abstract class AbstractEntityPersister // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private final SessionFactoryImplementor factory; + private final boolean canReadFromCache; + private final boolean canWriteToCache; private final EntityRegionAccessStrategy cacheAccessStrategy; private final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy; private final boolean isLazyPropertiesCacheable; @@ -512,9 +515,21 @@ public abstract class AbstractEntityPersister // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ this.factory = creationContext.getSessionFactory(); - this.cacheAccessStrategy = cacheAccessStrategy; - this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy; - isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable(); + + if ( creationContext.getSessionFactory().getSessionFactoryOptions().isSecondLevelCacheEnabled() ) { + 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.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 subclassIterator = persistentClass.getSubclassIterator(); + while ( subclassIterator.hasNext() ) { + final Subclass subclass = subclassIterator.next(); + if ( subclass.isCached() ) { + return true; + } + } + return false; + } + protected CacheEntryHelper buildCacheEntryHelper() { if ( cacheAccessStrategy == null ) { // 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 Object cacheKey = cache.generateCacheKey(id, this, session.getFactory(), session.getTenantIdentifier() ); final Object ce = CacheHelper.fromSharedCache( session, cacheKey, cache ); @@ -4293,8 +4324,18 @@ public abstract class AbstractEntityPersister return entityMetamodel; } + @Override + public boolean canReadFromCache() { + return canReadFromCache; + } + + @Override + public boolean canWriteToCache() { + return canWriteToCache; + } + public boolean hasCache() { - return cacheAccessStrategy != null; + return canWriteToCache; } public EntityRegionAccessStrategy getCacheAccessStrategy() { @@ -4467,7 +4508,7 @@ public abstract class AbstractEntityPersister } // 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 Object ck = cache.generateCacheKey( id, this, session.getFactory(), session.getTenantIdentifier() ); final Object ce = CacheHelper.fromSharedCache( session, ck, getCacheAccessStrategy() ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 6ed44a4902..5ef01a5489 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -503,9 +503,16 @@ public interface EntityPersister extends OptimisticCacheSource, EntityDefinition * Should lazy properties of this entity be cached? */ boolean isLazyPropertiesCacheable(); + + boolean canReadFromCache(); + boolean canWriteToCache(); + /** * Does this class have a cache. + * + * @deprecated Use {@link #canReadFromCache()} and/or {@link #canWriteToCache()} depending on need */ + @Deprecated boolean hasCache(); /** * Get the cache (optional operation) diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/cacheable/annotation/ConfigurationTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/cacheable/annotation/ConfigurationTest.java index f567527775..30722b0d68 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/cacheable/annotation/ConfigurationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/cacheable/annotation/ConfigurationTest.java @@ -13,7 +13,6 @@ import javax.persistence.EntityManagerFactory; import javax.persistence.SharedCacheMode; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.cache.internal.NoCachingRegionFactory; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.Environment; import org.hibernate.jpa.AvailableSettings; @@ -24,12 +23,11 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.testing.cache.CachingRegionFactory; import org.hibernate.testing.junit4.BaseUnitTestCase; - import org.junit.After; import org.junit.Test; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * 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 ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); } @Test @@ -65,13 +63,13 @@ public class ConfigurationTest extends BaseUnitTestCase { MetadataImplementor metadata = buildMetadata( SharedCacheMode.UNSPECIFIED ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); } @Test @@ -79,13 +77,13 @@ public class ConfigurationTest extends BaseUnitTestCase { MetadataImplementor metadata = buildMetadata( SharedCacheMode.ALL ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); - assertNotNull( pc.getCacheConcurrencyStrategy() ); + assertTrue( pc.isCached() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); - assertNotNull( pc.getCacheConcurrencyStrategy() ); + assertTrue( pc.isCached() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); - assertNotNull( pc.getCacheConcurrencyStrategy() ); + assertTrue( pc.isCached() ); } @Test @@ -93,13 +91,13 @@ public class ConfigurationTest extends BaseUnitTestCase { MetadataImplementor metadata = buildMetadata( SharedCacheMode.ENABLE_SELECTIVE ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); - assertNotNull( pc.getCacheConcurrencyStrategy() ); + assertTrue( pc.isCached() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); } @Test @@ -107,13 +105,13 @@ public class ConfigurationTest extends BaseUnitTestCase { MetadataImplementor metadata = buildMetadata( SharedCacheMode.DISABLE_SELECTIVE ); PersistentClass pc = metadata.getEntityBinding( ExplicitlyCacheableEntity.class.getName() ); - assertNotNull( pc.getCacheConcurrencyStrategy() ); + assertTrue( pc.isCached() ); pc = metadata.getEntityBinding( ExplicitlyNonCacheableEntity.class.getName() ); - assertNull( pc.getCacheConcurrencyStrategy() ); + assertFalse( pc.isCached() ); pc = metadata.getEntityBinding( NoCacheableAnnotationEntity.class.getName() ); - assertNotNull( pc.getCacheConcurrencyStrategy() ); + assertTrue( pc.isCached() ); } @SuppressWarnings("unchecked") diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java index 4536ceb455..1a83b48ae0 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java @@ -411,6 +411,16 @@ public class PersisterClassProviderTest { return false; } + @Override + public boolean canReadFromCache() { + return false; + } + + @Override + public boolean canWriteToCache() { + return false; + } + @Override public boolean hasCache() { return false; diff --git a/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheAnnotationTest.java b/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheAnnotationTest.java index ce48cdda80..8af2f60ee4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheAnnotationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheAnnotationTest.java @@ -33,6 +33,7 @@ import org.junit.Test; import org.jboss.logging.Logger; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -65,7 +66,7 @@ public class NonRootEntityWithCacheAnnotationTest { .buildMetadata(); assertTrue( triggerable.wasTriggered() ); - assertNull( ( metadata.getEntityBinding( AEntity.class.getName() ) ).getCacheConcurrencyStrategy() ); + assertFalse( metadata.getEntityBinding( AEntity.class.getName() ).isCached() ); serviceRegistry.destroy(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheableAnnotationTest.java b/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheableAnnotationTest.java index 7a0ed58916..f613fdd511 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheableAnnotationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cache/NonRootEntityWithCacheableAnnotationTest.java @@ -32,7 +32,7 @@ import org.junit.Test; import org.jboss.logging.Logger; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** @@ -64,8 +64,10 @@ public class NonRootEntityWithCacheableAnnotationTest { .addAnnotatedClass( AEntity.class ) .buildMetadata(); - assertTrue( triggerable.wasTriggered() ); - assertNull( ( metadata.getEntityBinding( AEntity.class.getName() ) ).getCacheConcurrencyStrategy() ); + assertFalse( metadata.getEntityBinding( ABase.class.getName() ).isCached() ); + assertTrue( metadata.getEntityBinding( AEntity.class.getName() ).isCached() ); + + assertFalse( triggerable.wasTriggered() ); serviceRegistry.destroy(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/cache/polymorphism/PolymorphicCacheTest.java b/hibernate-core/src/test/java/org/hibernate/test/cache/polymorphism/PolymorphicCacheTest.java index e07e9d8c65..6528a92116 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cache/polymorphism/PolymorphicCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cache/polymorphism/PolymorphicCacheTest.java @@ -46,7 +46,7 @@ public class PolymorphicCacheTest extends BaseCoreFunctionalTestCase { s.beginTransaction(); // See HHH-9107 try { - s.get( CachedItem2.class, item1.getId() ); + final CachedItem2 tmp = s.get( CachedItem2.class, item1.getId() ); fail( "Expected a WrongClassException to be thrown." ); } catch (WrongClassException e) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/DefaultCacheConcurrencyPropertyTest.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/DefaultCacheConcurrencyPropertyTest.java index f6c3e8772b..eeb40feac1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/DefaultCacheConcurrencyPropertyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/DefaultCacheConcurrencyPropertyTest.java @@ -28,7 +28,9 @@ import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * @author Gail Badner @@ -60,6 +62,8 @@ public class DefaultCacheConcurrencyPropertyTest extends BaseUnitTestCase { final SessionFactoryImplementor sf = (SessionFactoryImplementor) metadata.buildSessionFactory(); try { final EntityPersister persister = sf.getMetamodel().entityPersister( TheEntity.class.getName() ); + assertTrue( persister.canReadFromCache() ); + assertTrue( persister.canWriteToCache() ); assertNotNull( persister.getCacheAccessStrategy() ); } finally { diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index 18b2f23b34..03828bf933 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -384,6 +384,16 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { return false; } + @Override + public boolean canReadFromCache() { + return false; + } + + @Override + public boolean canWriteToCache() { + return false; + } + @Override public boolean hasCache() { return false; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/SubclassOnlyCachingTests.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/SubclassOnlyCachingTests.java index 92d4be8b44..a4a54885f1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/SubclassOnlyCachingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/SubclassOnlyCachingTests.java @@ -6,22 +6,89 @@ */ package org.hibernate.test.jpa.compliance.tck2_2; +import java.util.Map; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; +import javax.persistence.SharedCacheMode; import javax.persistence.Table; +import org.hibernate.Hibernate; import org.hibernate.boot.MetadataSources; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; 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 */ 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 protected void applyMetadataSources(MetadataSources sources) { @@ -31,11 +98,6 @@ public class SubclassOnlyCachingTests extends BaseNonConfigCoreFunctionalTestCas sources.addAnnotatedClass( Customer.class ); } - @Test - public void testOnlySubclassIsCached() { - - } - @Entity( name = "Person" ) @Table( name = "persons" ) @Inheritance( strategy = InheritanceType.SINGLE_TABLE ) @@ -43,18 +105,43 @@ public class SubclassOnlyCachingTests extends BaseNonConfigCoreFunctionalTestCas @Id public Integer id; 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 String employeeCode; 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() public static class Customer extends Person { public String erpCode; + + public Customer() { + } + + public Customer(Integer id, String name, String erpCode) { + super( id, name ); + this.erpCode = erpCode; + } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 9206731e50..61fab110f1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -653,6 +653,16 @@ public class CustomPersister implements EntityPersister { return true; } + @Override + public boolean canReadFromCache() { + return false; + } + + @Override + public boolean canWriteToCache() { + return false; + } + @Override public boolean isVersionPropertyGenerated() { return false; diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java index 8b60c5fdbb..d9d22b9391 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/discriminator/DiscriminatorMultiTenancyTest.java @@ -21,6 +21,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.RootClass; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool; @@ -70,7 +71,9 @@ public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase { ms.addAnnotatedClass(Customer.class); 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(); tool.injectServices(serviceRegistry); diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java index e3a8cca2a5..da363d9bee 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/AbstractSchemaBasedMultiTenancyTest.java @@ -19,6 +19,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.RootClass; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.Stoppable; @@ -74,7 +75,9 @@ public abstract class AbstractSchemaBasedMultiTenancyTest