From 6c4643f265355fc54bc47d5e3f339906e3421e35 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Fri, 23 Aug 2019 22:40:36 -0700 Subject: [PATCH] HHH-13586 : ClassCastException when using a single region name for both entity and query results (cherry picked from commit 2076c68ddff5dc39055e90e162a34c99c72261cb) --- .../cache/internal/EnabledCaching.java | 40 +++++++++++++------ .../hibernate/cache/spi/CacheImplementor.java | 4 +- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java index e7257ed0d0..9ce34807a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java @@ -48,6 +48,7 @@ import org.hibernate.pretty.MessageHelper; /** * @author Steve Ebersole * @author Strong Liu + * @author Gail Badner */ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildingContext { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EnabledCaching.class ); @@ -57,6 +58,10 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin private final Map regionsByName = new ConcurrentHashMap<>(); + // A map by name for QueryResultsRegion instances that have the same name as a Region + // in #regionsByName. + private final Map queryResultsRegionsByDuplicateName = new ConcurrentHashMap<>(); + private final Map entityAccessMap = new ConcurrentHashMap<>(); private final Map naturalIdAccessMap = new ConcurrentHashMap<>(); private final Map collectionAccessMap = new ConcurrentHashMap<>(); @@ -204,6 +209,8 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin @Override public Region getRegion(String regionName) { + // The Region in regionsByName has precedence over the + // QueryResultsRegion in #queryResultsRegionsByDuplicateName return regionsByName.get( regionName ); } @@ -488,12 +495,23 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin } protected QueryResultsCache makeQueryResultsRegionAccess(String regionName) { - final QueryResultsRegion region = (QueryResultsRegion) regionsByName.computeIfAbsent( + final Region region = regionsByName.computeIfAbsent( regionName, this::makeQueryResultsRegion ); + final QueryResultsRegion queryResultsRegion; + if ( QueryResultsRegion.class.isInstance( region ) ) { + queryResultsRegion = (QueryResultsRegion) region; + } + else { + // There was already a different type of Region with the same name. + queryResultsRegion = queryResultsRegionsByDuplicateName.computeIfAbsent( + regionName, + this::makeQueryResultsRegion + ); + } final QueryResultsCacheImpl regionAccess = new QueryResultsCacheImpl( - region, + queryResultsRegion, timestampsCache ); namedQueryResultsCacheMap.put( regionName, regionAccess ); @@ -502,20 +520,9 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin } protected QueryResultsRegion makeQueryResultsRegion(String regionName) { - // make sure there is not an existing domain-data region with that name.. - final Region existing = regionsByName.get( regionName ); - if ( existing != null ) { - if ( !QueryResultsRegion.class.isInstance( existing ) ) { - throw new IllegalStateException( "Cannot store both domain-data and query-result-data in the same region [" + regionName ); - } - - throw new IllegalStateException( "Illegal call to create QueryResultsRegion - one already existed" ); - } - return regionFactory.buildQueryResultsRegion( regionName, getSessionFactory() ); } - @Override public Set getCacheRegionNames() { return regionsByName.keySet(); @@ -524,6 +531,10 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin @Override public void evictRegion(String regionName) { getRegion( regionName ).clear(); + final QueryResultsRegion queryResultsRegionWithDuplicateName = queryResultsRegionsByDuplicateName.get( regionName ); + if ( queryResultsRegionWithDuplicateName != null ) { + queryResultsRegionWithDuplicateName.clear(); + } } @Override @@ -545,6 +556,9 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin for ( Region region : regionsByName.values() ) { region.destroy(); } + for ( Region region : queryResultsRegionsByDuplicateName.values() ) { + region.destroy(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java index 00a28c7b99..c77b13db7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheImplementor.java @@ -52,7 +52,9 @@ public interface CacheImplementor extends Service, Cache, org.hibernate.engine.s void prime(Set cacheRegionConfigs); /** - * Get a cache Region by name + * Get a cache Region by name. If there is both a {@link DomainDataRegion} + * and a {@link QueryResultsRegion} with the specified name, then the + * {@link DomainDataRegion} will be returned. * * @apiNote It is only valid to call this method after {@link #prime} has * been performed