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:
+ *
+ * - {@link org.hibernate.engine.spi.PersistentAttributeInterceptable}
+ * - {@link org.hibernate.engine.spi.ManagedEntity}
+ * - {@link org.hibernate.engine.spi.SelfDirtinessTracker}
+ * - {@link org.hibernate.engine.spi.Managed}
+ * - {@link org.hibernate.proxy.HibernateProxy}
+ *
+ *
+ * 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;