From 3028299b4a6f41a60d3955ea9a1411d4681b7ff0 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 29 Nov 2022 22:44:27 +0000 Subject: [PATCH] HHH-15790 New dispatch approach to manage type checks of enhanced entities --- .../internal/bytebuddy/EnhancerImpl.java | 67 +---- .../PersistentAttributeTransformer.java | 7 +- .../engine/internal/ManagedTypeHelper.java | 251 +++++++++++------- .../internal/StatefulPersistenceContext.java | 2 +- .../hibernate/engine/spi/EnhancedEntity.java | 28 -- .../org/hibernate/engine/spi/Managed.java | 13 +- .../engine/spi/ManagedComposite.java | 11 + .../hibernate/engine/spi/ManagedEntity.java | 11 + .../engine/spi/ManagedMappedSuperclass.java | 11 + .../spi/PersistentAttributeInterceptable.java | 13 +- .../spi/PrimeAmongSecondarySupertypes.java | 48 ++++ .../engine/spi/SelfDirtinessTracker.java | 13 +- ...rtyAccessStrategyResolverStandardImpl.java | 2 +- 13 files changed, 290 insertions(+), 187 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/engine/spi/EnhancedEntity.java create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java index ac9308117d..0f5bd82c65 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java @@ -36,7 +36,6 @@ import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterc import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; import org.hibernate.engine.spi.CompositeOwner; import org.hibernate.engine.spi.CompositeTracker; -import org.hibernate.engine.spi.EnhancedEntity; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker; import org.hibernate.engine.spi.Managed; @@ -179,7 +178,6 @@ public class EnhancerImpl implements Enhancer { log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() ); return null; } - final EnhancementStatus es = new EnhancementStatus( managedCtClass.getName() ); builder = builder.annotateType( HIBERNATE_VERSION_ANNOTATION ); @@ -188,7 +186,6 @@ public class EnhancerImpl implements Enhancer { builder = builder.implement( ManagedEntity.class ) .defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, Object.class, Visibility.PUBLIC ) .intercept( FixedValue.self() ); - es.enabledInterfaceManagedEntity(); builder = addFieldWithGetterAndSetter( builder, @@ -212,7 +209,7 @@ public class EnhancerImpl implements Enhancer { EnhancerConstants.NEXT_SETTER_NAME ); - builder = addInterceptorHandling( builder, managedCtClass, es ); + builder = addInterceptorHandling( builder, managedCtClass ); if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) { List collectionFields = collectCollectionFields( managedCtClass ); @@ -235,7 +232,6 @@ public class EnhancerImpl implements Enhancer { .intercept( implementationSuspendDirtyTracking ) .defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC ) .intercept( implementationGetCollectionTrackerWithoutCollections ); - es.enabledInterfaceSelfDirtinessTracker(); } else { //TODO es.enableInterfaceExtendedSelfDirtinessTracker ? Careful with consequences.. @@ -333,13 +329,13 @@ public class EnhancerImpl implements Enhancer { } } - return createTransformer( managedCtClass ).applyTo( builder, es ); + return createTransformer( managedCtClass ).applyTo( builder ); } else if ( enhancementContext.isCompositeClass( managedCtClass ) ) { log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() ); builder = builder.implement( ManagedComposite.class ); - builder = addInterceptorHandling( builder, managedCtClass, es ); + builder = addInterceptorHandling( builder, managedCtClass ); if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) { builder = builder.implement( CompositeTracker.class ) @@ -366,17 +362,17 @@ public class EnhancerImpl implements Enhancer { .intercept( implementationClearOwner ); } - return createTransformer( managedCtClass ).applyTo( builder, es ); + return createTransformer( managedCtClass ).applyTo( builder ); } else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() ); builder = builder.implement( ManagedMappedSuperclass.class ); - return createTransformer( managedCtClass ).applyTo( builder, es ); + return createTransformer( managedCtClass ).applyTo( builder ); } else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) { log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() ); - return createTransformer( managedCtClass ).applyExtended( builder, es ); + return createTransformer( managedCtClass ).applyExtended( builder ); } else { log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() ); @@ -398,13 +394,12 @@ public class EnhancerImpl implements Enhancer { return false; } - private DynamicType.Builder addInterceptorHandling(DynamicType.Builder builder, TypeDescription managedCtClass, EnhancementStatus es) { + private DynamicType.Builder addInterceptorHandling(DynamicType.Builder builder, TypeDescription managedCtClass) { // interceptor handling is only needed if class has lazy-loadable attributes if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) { log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() ); builder = builder.implement( PersistentAttributeInterceptable.class ); - es.enabledInterfacePersistentAttributeInterceptable(); builder = addFieldWithGetterAndSetter( builder, @@ -625,52 +620,4 @@ public class EnhancerImpl implements Enhancer { } } - /** - * Attempt to keep track of which interfaces are being applied, - * so to attempt dodging the performance implications of for https://bugs.openjdk.org/browse/JDK-8180450 - * We're optimising for the case in which entities are fully enhanced. - */ - final static class EnhancementStatus { - - private final String typeName; - private boolean managedEntity = false; - private boolean selfDirtynessTracker = false; - private boolean persistentAttributeInterceptable = false; - private boolean applied = false; - - public EnhancementStatus(String typeName) { - this.typeName = typeName; - } - - public void enabledInterfaceManagedEntity() { - this.managedEntity = true; - } - - public void enabledInterfaceSelfDirtinessTracker() { - this.selfDirtynessTracker = true; - } - - public void enabledInterfacePersistentAttributeInterceptable() { - this.persistentAttributeInterceptable = true; - } - - public DynamicType.Builder applySuperInterfaceOptimisations(DynamicType.Builder builder) { - if ( applied ) { - throw new IllegalStateException("Should not apply super-interface optimisations twice"); - } - else { - applied = true; - if ( managedEntity && persistentAttributeInterceptable && selfDirtynessTracker ) { - log.debugf( "Applying Enhancer optimisations for type [%s]; adding EnhancedEntity as additional marker.", typeName ); - return builder.implement( EnhancedEntity.class ); - } - else { - log.debugf( "Applying Enhancer optimisations for type [%s]; NOT enabling EnhancedEntity as additional marker.", typeName ); - } - //TODO consider applying a marker for other combinations of interfaces as well? - } - return builder; - } - } - } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java index ad0a6fc88d..2b7cccac1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java @@ -224,8 +224,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla return null; } - DynamicType.Builder applyTo(DynamicType.Builder builder, EnhancerImpl.EnhancementStatus es) { - builder = es.applySuperInterfaceOptimisations(builder); + DynamicType.Builder applyTo(DynamicType.Builder builder) { boolean compositeOwner = false; // Remove the private modifier from the constructor, which allows to create a better InstantiationOptimizer @@ -295,7 +294,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla } if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) { - builder = applyExtended( builder, es ); + builder = applyExtended( builder ); } return builder; @@ -344,7 +343,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla } } - DynamicType.Builder applyExtended(DynamicType.Builder builder, EnhancerImpl.EnhancementStatus es) { + DynamicType.Builder applyExtended(DynamicType.Builder builder) { AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper enhancer = new FieldAccessEnhancer( managedCtClass, enhancementContext, classPool ); return builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, enhancer ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java index f8c3b8ad87..de4bb6bab1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java @@ -6,86 +6,74 @@ */ package org.hibernate.engine.internal; -import org.hibernate.engine.spi.EnhancedEntity; +import org.hibernate.engine.spi.PrimeAmongSecondarySupertypes; import org.hibernate.engine.spi.Managed; import org.hibernate.engine.spi.ManagedEntity; +import org.hibernate.engine.spi.ManagedMappedSuperclass; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.SelfDirtinessTracker; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Consumer; /** * This is a helper to encapsulate an optimal strategy to execute type checks * for interfaces which attempts to avoid the performance issues tracked - * as https://bugs.openjdk.org/browse/JDK-8180450 ; - * the problem is complex and best understood by reading the OpenJDK tracker; + * as JDK-8180450; + * the problem is complex and explained better on the OpenJDK tracker; * we'll focus on a possible solution here. - *

+ *

* To avoid polluting the secondary super-type cache, the important aspect is to * not switch types repeatedly for the same concrete object; using a Java * agent which was developed for this purpose (https://github.com/franz1981/type-pollution-agent) * we identified a strong case with Hibernate ORM is triggered when the entities are - * using bytecode enhancement, as they are being checked by a set of interfaces: - * {@see org.hibernate.engine.spi.PersistentAttributeInterceptable} - * {@see org.hibernate.engine.spi.ManagedEntity} - * {@see org.hibernate.engine.spi.SelfDirtinessTracker} - * {@see org.hibernate.engine.spi.Managed} - * With our domain knowledge, we bet on the assumption that either enhancement isn't being - * used at all, OR that when enhancement is being used, there is a strong likelyhood for - * all of these supertypes to be have been injected into the managed objected of the domain - * model (this isn't a certainty as otherwise we'd not have multiple interfaces to separate - * these), but we're working based on the assumption so to at least optimise for what - * we expect being a very common configuration. - * (At this time we won't optimise also embeddables and other corner cases, which will - * need to be looked at separately). - * We therefore introduce a new marker interface {@see EnhancedEntity}, which extends - * all these other contracts, and have the enhancer tool apply it when all other interfaces - * have been applied. - * This then allows to check always and consistently for this type only; as fallback - * path, we perform the "traditional" operation as it would have been before this patch. + * using bytecode enhancement, as they are being frequently checked for compatibility with + * the following interfaces: + *

+ *

+ * Some additional interfaces are involved in bytecode enhancement (such as {@link ManagedMappedSuperclass}), + * but some might not be managed here as there was no evidence of them triggering the problem; + * this might change after further testing. + *

+ * The approach we pursue is to have all these internal interfaces extend a single + * interface {@link PrimeAmongSecondarySupertypes} which then exposes a type widening + * contract; this allows to consistently cast to {@code PrimeAmongSecondarySupertypes} exclusively + * and avoid any further type checks; since the cast consistently happens on this interface + * we avoid polluting the secondary super type cache described in JDK-8180450. + *

+ * This presents two known drawbacks: + *

+ * 1# we do assume such user entities aren't being used via interfaces in hot user code; + * this is typically not the case based on our experience of Hibernate usage, but it + * can't be ruled out. + *

+ * 2# we're introducing virtual dispatch calls which are likely going to be megamorphic; + * this is not great but we assume it's far better to avoid the scalability issue. + * * @author Sanne Grinovero */ public final class ManagedTypeHelper { + private static final ClassValue typeMetaCache = new ClassValue<>() { + @Override + protected TypeMeta computeValue(Class type) { + return new TypeMeta(type); + } + }; + /** * @param type * @return true if and only if the type is assignable to a {@see Managed} type. */ public static boolean isManagedType(final Class type) { - return EnhancedEntity.class.isAssignableFrom( type ) || Managed.class.isAssignableFrom( type ); - } - - /** - * @param entity - * @return true if and only if the entity implements {@see ManagedEntity} - */ - public static boolean isManagedEntity(final Object entity) { - return entity instanceof EnhancedEntity || entity instanceof ManagedEntity; - } - - /** - * @param type - * @return true if and only if the type is assignable to a {@see PersistentAttributeInterceptable} type. - */ - public static boolean isPersistentAttributeInterceptableType(final Class type) { - return EnhancedEntity.class.isAssignableFrom( type ) || PersistentAttributeInterceptable.class.isAssignableFrom( type ); - } - - /** - * @param entity - * @return true if and only if the entity implements {@see PersistentAttributeInterceptable} - */ - public static boolean isPersistentAttributeInterceptable(final Object entity) { - return entity instanceof EnhancedEntity || entity instanceof PersistentAttributeInterceptable; - } - - /** - * @param entity - * @return true if and only if the entity implements {@see SelfDirtinessTracker} - */ - public static boolean isSelfDirtinessTracker(final Object entity) { - return entity instanceof EnhancedEntity || entity instanceof SelfDirtinessTracker; + return typeMetaCache.get( type ).isManagedType; } /** @@ -93,9 +81,54 @@ public final class ManagedTypeHelper { * @return true if and only if the type is assignable to a {@see SelfDirtinessTracker} type. */ public static boolean isSelfDirtinessTrackerType(final Class type) { - return EnhancedEntity.class.isAssignableFrom( type ) || SelfDirtinessTracker.class.isAssignableFrom( type ); + return typeMetaCache.get( type ).isSelfDirtinessTrackerType; } + /** + * @param type + * @return true if and only if the type is assignable to a {@see PersistentAttributeInterceptable} type. + */ + public static boolean isPersistentAttributeInterceptableType(final Class type) { + return typeMetaCache.get( type ).isPersistentAttributeInterceptable; + } + + /** + * @param entity + * @return true if and only if the entity implements {@see ManagedEntity} + */ + public static boolean isManagedEntity(final Object entity) { + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + return t.asManagedEntity() != null; + } + return false; + } + + /** + * @param entity + * @return true if and only if the entity implements {@see PersistentAttributeInterceptable} + */ + public static boolean isPersistentAttributeInterceptable(final Object entity) { + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + return t.asPersistentAttributeInterceptable() != null; + } + return false; + } + + /** + * @param entity + * @return true if and only if the entity implements {@see SelfDirtinessTracker} + */ + public static boolean isSelfDirtinessTracker(final Object entity) { + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + return t.asSelfDirtinessTracker() != null; + } + return false; + } + + /** * Helper to execute an action on an entity, but exclusively if it's implementing the {@see PersistentAttributeInterceptable} * interface. Otherwise no action is performed. @@ -109,13 +142,12 @@ public final class ManagedTypeHelper { final Object entity, final BiConsumer action, final T optionalParam) { - if ( entity instanceof EnhancedEntity ) { - EnhancedEntity e = (EnhancedEntity) entity; - action.accept( e, optionalParam ); - } - else if ( entity instanceof PersistentAttributeInterceptable ) { - PersistentAttributeInterceptable e = (PersistentAttributeInterceptable) entity; - action.accept( e, optionalParam ); + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + final PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + final PersistentAttributeInterceptable e = t.asPersistentAttributeInterceptable(); + if ( e != null ) { + action.accept( e, optionalParam ); + } } } @@ -127,13 +159,12 @@ public final class ManagedTypeHelper { * @param action */ public static void processIfSelfDirtinessTracker(final Object entity, final Consumer action) { - if ( entity instanceof EnhancedEntity ) { - EnhancedEntity e = (EnhancedEntity) entity; - action.accept( e ); - } - else if ( entity instanceof SelfDirtinessTracker ) { - SelfDirtinessTracker e = (SelfDirtinessTracker) entity; - action.accept( e ); + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + final PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + final SelfDirtinessTracker e = t.asSelfDirtinessTracker(); + if ( e != null ) { + action.accept( e ); + } } } @@ -151,13 +182,12 @@ public final class ManagedTypeHelper { final Object entity, final BiConsumer action, final T optionalParam) { - if ( entity instanceof EnhancedEntity ) { - EnhancedEntity e = (EnhancedEntity) entity; - action.accept( e, optionalParam ); - } - else if ( entity instanceof SelfDirtinessTracker ) { - SelfDirtinessTracker e = (SelfDirtinessTracker) entity; - action.accept( e, optionalParam ); + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + final PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + final SelfDirtinessTracker e = t.asSelfDirtinessTracker(); + if ( e != null ) { + action.accept( e, optionalParam ); + } } } @@ -169,12 +199,34 @@ public final class ManagedTypeHelper { * @throws ClassCastException if it's not of the right type */ public static PersistentAttributeInterceptable asPersistentAttributeInterceptable(final Object entity) { - if ( entity instanceof EnhancedEntity ) { - return (EnhancedEntity) entity; + Objects.requireNonNull( entity ); + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + final PersistentAttributeInterceptable e = t.asPersistentAttributeInterceptable(); + if ( e != null ) { + return e; + } } - else { - return (PersistentAttributeInterceptable) entity; + throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to PersistentAttributeInterceptable" ); + } + + /** + * Cast the object to HibernateProxy + * (using this is highly preferrable over a direct cast) + * @param entity the entity to cast + * @return the same instance after casting + * @throws ClassCastException if it's not of the right type + */ + public static HibernateProxy asHibernateProxy(final Object entity) { + Objects.requireNonNull( entity ); + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + final HibernateProxy e = t.asHibernateProxy(); + if ( e != null ) { + return e; + } } + throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to HibernateProxy" ); } /** @@ -185,12 +237,15 @@ public final class ManagedTypeHelper { * @throws ClassCastException if it's not of the right type */ public static ManagedEntity asManagedEntity(final Object entity) { - if ( entity instanceof EnhancedEntity ) { - return (EnhancedEntity) entity; - } - else { - return (ManagedEntity) entity; + Objects.requireNonNull( entity ); + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + final ManagedEntity e = t.asManagedEntity(); + if ( e != null ) { + return e; + } } + throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to ManagedEntity" ); } /** @@ -201,11 +256,27 @@ public final class ManagedTypeHelper { * @throws ClassCastException if it's not of the right type */ public static SelfDirtinessTracker asSelfDirtinessTracker(final Object entity) { - if ( entity instanceof EnhancedEntity ) { - return (EnhancedEntity) entity; + Objects.requireNonNull( entity ); + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + final SelfDirtinessTracker e = t.asSelfDirtinessTracker(); + if ( e != null ) { + return e; + } } - else { - return (SelfDirtinessTracker) entity; + throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to SelfDirtinessTracker" ); + } + + private static final class TypeMeta { + final boolean isManagedType; + final boolean isSelfDirtinessTrackerType; + final boolean isPersistentAttributeInterceptable; + + TypeMeta(final Class type) { + Objects.requireNonNull( type ); + this.isManagedType = Managed.class.isAssignableFrom( type ); + this.isSelfDirtinessTrackerType = SelfDirtinessTracker.class.isAssignableFrom( type ); + this.isPersistentAttributeInterceptable = PersistentAttributeInterceptable.class.isAssignableFrom( type ); } } 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 0b31d75ac0..4a9c93652a 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 @@ -265,7 +265,7 @@ public class StatefulPersistenceContext implements PersistenceContext { } private static void processEntityOnClear(final Object entity) { - //type-cache-pollution agent: always check for EnhancedEntity type first. + //type-cache-pollution agent: it's crucial to use the ManagedTypeHelper rather than attempting a direct cast ManagedTypeHelper.processIfPersistentAttributeInterceptable( entity, StatefulPersistenceContext::unsetSession, null ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/EnhancedEntity.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/EnhancedEntity.java deleted file mode 100644 index 9f785b8162..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/EnhancedEntity.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later - * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html - */ -package org.hibernate.engine.spi; - -/** - * This is a special marker interface designed to represent the union of several traits: - * - ManagedEntity - * - Managed - * - PersistentAttributeInterceptable - * - SelfDirtinessTracker - * The need for such a "union" isn't natural in the Java language, but represents a technicality - * we need to bypass performance issues caused by https://bugs.openjdk.org/browse/JDK-8180450 - * @see org.hibernate.engine.internal.ManagedTypeHelper - * @see org.hibernate.engine.spi.Managed - * @see org.hibernate.engine.spi.ManagedEntity - * @see PersistentAttributeInterceptable - * @see SelfDirtinessTracker - */ -public interface EnhancedEntity extends ManagedEntity, PersistentAttributeInterceptable, SelfDirtinessTracker { - - //TODO what about ExtendedSelfDirtinessTracker ? - //TODO CompositeTracker, ManagedMappedSuperclass - -} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/Managed.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/Managed.java index 20a7a08b63..1259703567 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/Managed.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/Managed.java @@ -22,5 +22,16 @@ package org.hibernate.engine.spi; * * @author Steve Ebersole */ -public interface Managed { +public interface Managed extends PrimeAmongSecondarySupertypes { + + /** + * Special internal contract to optimize type checking + * @see PrimeAmongSecondarySupertypes + * @return this same instance + */ + @Override + default Managed asManaged() { + return this; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedComposite.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedComposite.java index a6a8d56e96..ac3aff50b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedComposite.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedComposite.java @@ -12,4 +12,15 @@ package org.hibernate.engine.spi; * @author Steve Ebersole */ public interface ManagedComposite extends Managed { + + /** + * Special internal contract to optimize type checking + * @see PrimeAmongSecondarySupertypes + * @return this same instance + */ + @Override + default ManagedComposite asManagedComposite() { + return this; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedEntity.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedEntity.java index bd8c4501fd..67dc6ccfc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedEntity.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedEntity.java @@ -79,4 +79,15 @@ public interface ManagedEntity extends Managed { * @param next The next entry */ void $$_hibernate_setNextManagedEntity(ManagedEntity next); + + /** + * Special internal contract to optimize type checking + * @see PrimeAmongSecondarySupertypes + * @return this same instance + */ + @Override + default ManagedEntity asManagedEntity() { + return this; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedMappedSuperclass.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedMappedSuperclass.java index 3932ab2af1..cedc9a6314 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedMappedSuperclass.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ManagedMappedSuperclass.java @@ -12,4 +12,15 @@ package org.hibernate.engine.spi; * @author Luis Barreiro */ public interface ManagedMappedSuperclass extends Managed { + + /** + * Special internal contract to optimize type checking + * @see PrimeAmongSecondarySupertypes + * @return this same instance + */ + @Override + default ManagedMappedSuperclass asManagedMappedSuperclass() { + return this; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistentAttributeInterceptable.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistentAttributeInterceptable.java index 6b9e1465df..6a21b4e2f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistentAttributeInterceptable.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistentAttributeInterceptable.java @@ -9,7 +9,18 @@ package org.hibernate.engine.spi; /** * @author Steve Ebersole */ -public interface PersistentAttributeInterceptable { +public interface PersistentAttributeInterceptable extends PrimeAmongSecondarySupertypes { PersistentAttributeInterceptor $$_hibernate_getInterceptor(); void $$_hibernate_setInterceptor(PersistentAttributeInterceptor interceptor); + + /** + * Special internal contract to optimize type checking + * @see PrimeAmongSecondarySupertypes + * @return this same instance + */ + @Override + default PersistentAttributeInterceptable asPersistentAttributeInterceptable() { + return this; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java new file mode 100644 index 0000000000..25e78bbc33 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.engine.spi; + +import org.hibernate.engine.internal.ManagedTypeHelper; + +/** + * For a full explanation of the purpose of this interface + * see {@link ManagedTypeHelper}. + * This is an internal, private marking interface; it's exposed in the spi + * package as bytecode enhanced usercode needs to be able to refer to it. + * + * @author Sanne Grinovero + */ +public interface PrimeAmongSecondarySupertypes { + + default ManagedEntity asManagedEntity() { + return null; + } + + default PersistentAttributeInterceptable asPersistentAttributeInterceptable() { + return null; + } + + default SelfDirtinessTracker asSelfDirtinessTracker() { + return null; + } + + //Included for consistency but doesn't seem to be used? + default Managed asManaged() { + return null; + } + + //Included for consistency but doesn't seem to be used? + default ManagedComposite asManagedComposite() { + return null; + } + + //Included for consistency but doesn't seem to be used? + default ManagedMappedSuperclass asManagedMappedSuperclass() { + return null; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java index e752c17805..5f20eb5e98 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java @@ -18,7 +18,7 @@ import org.hibernate.bytecode.enhance.spi.CollectionTracker; * * @author Ståle W. Pedersen */ -public interface SelfDirtinessTracker { +public interface SelfDirtinessTracker extends PrimeAmongSecondarySupertypes { /** * Have any of the entity's persistent attributes changed? * @@ -53,4 +53,15 @@ public interface SelfDirtinessTracker { * Get access to the CollectionTracker */ CollectionTracker $$_hibernate_getCollectionTracker(); + + /** + * Special internal contract to optimize type checking + * @see PrimeAmongSecondarySupertypes + * @return this same instance + */ + @Override + default SelfDirtinessTracker asSelfDirtinessTracker() { + return this; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java index 0331cba783..142a39404d 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/PropertyAccessStrategyResolverStandardImpl.java @@ -38,7 +38,7 @@ public class PropertyAccessStrategyResolverStandardImpl implements PropertyAcces if ( BuiltInPropertyAccessStrategies.BASIC.getExternalName().equals( explicitAccessStrategyName ) || BuiltInPropertyAccessStrategies.FIELD.getExternalName().equals( explicitAccessStrategyName ) || BuiltInPropertyAccessStrategies.MIXED.getExternalName().equals( explicitAccessStrategyName ) ) { - //type-cache-pollution agent: always check for EnhancedEntity type first. + //type-cache-pollution agent: it's crucial to use the ManagedTypeHelper rather than attempting a direct cast if ( isManagedType( containerClass ) ) { // PROPERTY (BASIC) and MIXED are not valid for bytecode enhanced entities... return PropertyAccessStrategyEnhancedImpl.INSTANCE;