HHH-13586 : ClassCastException when using a single region name for both entity and query results

(cherry picked from commit 2076c68ddf)
This commit is contained in:
Gail Badner 2019-08-23 22:40:36 -07:00 committed by gbadner
parent 1f48df3ee5
commit 6c4643f265
2 changed files with 30 additions and 14 deletions

View File

@ -48,6 +48,7 @@ import org.hibernate.pretty.MessageHelper;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
* @author Strong Liu * @author Strong Liu
* @author Gail Badner
*/ */
public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildingContext { public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildingContext {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EnabledCaching.class ); private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EnabledCaching.class );
@ -57,6 +58,10 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin
private final Map<String,Region> regionsByName = new ConcurrentHashMap<>(); private final Map<String,Region> regionsByName = new ConcurrentHashMap<>();
// A map by name for QueryResultsRegion instances that have the same name as a Region
// in #regionsByName.
private final Map<String, QueryResultsRegion> queryResultsRegionsByDuplicateName = new ConcurrentHashMap<>();
private final Map<NavigableRole,EntityDataAccess> entityAccessMap = new ConcurrentHashMap<>(); private final Map<NavigableRole,EntityDataAccess> entityAccessMap = new ConcurrentHashMap<>();
private final Map<NavigableRole,NaturalIdDataAccess> naturalIdAccessMap = new ConcurrentHashMap<>(); private final Map<NavigableRole,NaturalIdDataAccess> naturalIdAccessMap = new ConcurrentHashMap<>();
private final Map<NavigableRole,CollectionDataAccess> collectionAccessMap = new ConcurrentHashMap<>(); private final Map<NavigableRole,CollectionDataAccess> collectionAccessMap = new ConcurrentHashMap<>();
@ -204,6 +209,8 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin
@Override @Override
public Region getRegion(String regionName) { public Region getRegion(String regionName) {
// The Region in regionsByName has precedence over the
// QueryResultsRegion in #queryResultsRegionsByDuplicateName
return regionsByName.get( regionName ); return regionsByName.get( regionName );
} }
@ -488,12 +495,23 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin
} }
protected QueryResultsCache makeQueryResultsRegionAccess(String regionName) { protected QueryResultsCache makeQueryResultsRegionAccess(String regionName) {
final QueryResultsRegion region = (QueryResultsRegion) regionsByName.computeIfAbsent( final Region region = regionsByName.computeIfAbsent(
regionName, regionName,
this::makeQueryResultsRegion 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( final QueryResultsCacheImpl regionAccess = new QueryResultsCacheImpl(
region, queryResultsRegion,
timestampsCache timestampsCache
); );
namedQueryResultsCacheMap.put( regionName, regionAccess ); namedQueryResultsCacheMap.put( regionName, regionAccess );
@ -502,20 +520,9 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin
} }
protected QueryResultsRegion makeQueryResultsRegion(String regionName) { 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() ); return regionFactory.buildQueryResultsRegion( regionName, getSessionFactory() );
} }
@Override @Override
public Set<String> getCacheRegionNames() { public Set<String> getCacheRegionNames() {
return regionsByName.keySet(); return regionsByName.keySet();
@ -524,6 +531,10 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin
@Override @Override
public void evictRegion(String regionName) { public void evictRegion(String regionName) {
getRegion( regionName ).clear(); getRegion( regionName ).clear();
final QueryResultsRegion queryResultsRegionWithDuplicateName = queryResultsRegionsByDuplicateName.get( regionName );
if ( queryResultsRegionWithDuplicateName != null ) {
queryResultsRegionWithDuplicateName.clear();
}
} }
@Override @Override
@ -545,6 +556,9 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin
for ( Region region : regionsByName.values() ) { for ( Region region : regionsByName.values() ) {
region.destroy(); region.destroy();
} }
for ( Region region : queryResultsRegionsByDuplicateName.values() ) {
region.destroy();
}
} }

View File

@ -52,7 +52,9 @@ public interface CacheImplementor extends Service, Cache, org.hibernate.engine.s
void prime(Set<DomainDataRegionConfig> cacheRegionConfigs); void prime(Set<DomainDataRegionConfig> 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 * @apiNote It is only valid to call this method after {@link #prime} has
* been performed * been performed