diff --git a/src/main/java/org/hibernate/boot/models/bind/internal/binders/EntityTypeBinder.java b/src/main/java/org/hibernate/boot/models/bind/internal/binders/EntityTypeBinder.java index 108e736..0640846 100644 --- a/src/main/java/org/hibernate/boot/models/bind/internal/binders/EntityTypeBinder.java +++ b/src/main/java/org/hibernate/boot/models/bind/internal/binders/EntityTypeBinder.java @@ -69,6 +69,7 @@ import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; import jakarta.persistence.InheritanceType; +import jakarta.persistence.SharedCacheMode; import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER; import static org.hibernate.boot.models.bind.internal.binders.IdentifierBinder.bindIdentifier; @@ -452,9 +453,39 @@ protected void prepareBinding(ModelBinders modelBinders) { bindDiscriminatorValue( getManagedType(), getTypeBinding(), modelBinders, getBindingState(), getOptions(), getBindingContext() ); } + bindCacheable( getManagedType(), getTypeBinding(), modelBinders, getOptions(), getBindingState(), getBindingContext() ); + super.prepareBinding( modelBinders ); } + private static void bindCacheable( + EntityTypeMetadata managedType, + PersistentClass typeBinding, + ModelBinders modelBinders, + BindingOptions options, + BindingState bindingState, + BindingContext bindingContext) { + final AnnotationUsage cacheableAnn = managedType.getClassDetails().getAnnotationUsage( Cacheable.class ); + final SharedCacheMode sharedCacheMode = bindingState.getMetadataBuildingContext() + .getBuildingOptions() + .getSharedCacheMode(); + typeBinding.setCached( isCacheable( sharedCacheMode, cacheableAnn ) ); + } + + private static boolean isCacheable(SharedCacheMode sharedCacheMode, AnnotationUsage explicitCacheableAnn) { + return switch ( sharedCacheMode ) { + // all entities should be cached + case ALL -> true; + // Hibernate defaults to ENABLE_SELECTIVE, the only sensible setting + // only entities with @Cacheable(true) should be cached + case ENABLE_SELECTIVE, UNSPECIFIED -> explicitCacheableAnn != null && explicitCacheableAnn.getBoolean( "value" ); + // only entities with @Cacheable(false) should not be cached + case DISABLE_SELECTIVE -> explicitCacheableAnn == null || explicitCacheableAnn.getBoolean( "value" ); + // treat both NONE and UNSPECIFIED the same + default -> false; + }; + } + protected BasicValue getDiscriminatorMapping() { if ( binding instanceof RootClass rootClass ) { return (BasicValue) rootClass.getDiscriminator(); @@ -582,7 +613,7 @@ private static void bindVersion( } } - private void bindTenantId( + private static void bindTenantId( EntityTypeMetadata managedType, RootClass typeBinding, ModelBinders modelBinders, diff --git a/src/test/java/org/hibernate/models/orm/bind/cache/SimpleCachingTests.java b/src/test/java/org/hibernate/models/orm/bind/cache/SimpleCachingTests.java new file mode 100644 index 0000000..61bb5b3 --- /dev/null +++ b/src/test/java/org/hibernate/models/orm/bind/cache/SimpleCachingTests.java @@ -0,0 +1,75 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.bind.cache; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.NaturalIdCache; +import org.hibernate.boot.internal.InFlightMetadataCollectorImpl; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.RootClass; + +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.ServiceRegistryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Cacheable; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.Table; + +import static jakarta.persistence.InheritanceType.JOINED; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE; +import static org.hibernate.models.orm.bind.BindingTestingHelper.checkDomainModel; + +/** + * @author Steve Ebersole + */ +public class SimpleCachingTests { + @SuppressWarnings("JUnitMalformedDeclaration") + @Test + @ServiceRegistry + void simpleTest(ServiceRegistryScope scope) { + checkDomainModel( + (context) -> { + final var metadataCollector = context.getMetadataCollector(); + final RootClass entityBinding = (RootClass) metadataCollector.getEntityBinding( CacheableEntity.class.getName() ); + assertThat( entityBinding.isCached() ).isTrue(); + assertThat( entityBinding.getCacheRegionName() ).isEqualTo( "org.hibernate.testing.entity" ); + assertThat( entityBinding.getCacheConcurrencyStrategy() ).isEqualToIgnoringCase( "read-write" ); + assertThat( entityBinding.getNaturalIdCacheRegionName() ).isEqualTo( "org.hibernate.testing.natural-id" ); + + final PersistentClass subEntityBinding = metadataCollector.getEntityBinding( CacheableEntitySub.class.getName() ); + assertThat( subEntityBinding.isCached() ).isFalse(); + }, + scope.getRegistry(), + CacheableEntity.class, + CacheableEntitySub.class + ); + } + + @Entity(name="CacheableEntity") + @Table(name="CacheableEntity") + @Cacheable + @Cache( region = "org.hibernate.testing.entity", usage = READ_WRITE) + @NaturalIdCache( region = "org.hibernate.testing.natural-id" ) + @Inheritance(strategy = JOINED) + public static class CacheableEntity { + @Id + private Integer id; + private String name; + } + + @Entity(name="CacheableEntitySub") + @Table(name="CacheableEntitySub") + @Cacheable(false) + public static class CacheableEntitySub extends CacheableEntity { + private String someText; + } +}