HHH-15790 New dispatch approach to manage type checks of enhanced entities

This commit is contained in:
Sanne Grinovero 2022-11-29 22:44:27 +00:00 committed by Sanne Grinovero
parent c595347803
commit 3028299b4a
13 changed files with 290 additions and 187 deletions

View File

@ -36,7 +36,6 @@ import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterc
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState;
import org.hibernate.engine.spi.CompositeOwner; import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker; import org.hibernate.engine.spi.CompositeTracker;
import org.hibernate.engine.spi.EnhancedEntity;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker; import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker;
import org.hibernate.engine.spi.Managed; 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() ); log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() );
return null; return null;
} }
final EnhancementStatus es = new EnhancementStatus( managedCtClass.getName() );
builder = builder.annotateType( HIBERNATE_VERSION_ANNOTATION ); builder = builder.annotateType( HIBERNATE_VERSION_ANNOTATION );
@ -188,7 +186,6 @@ public class EnhancerImpl implements Enhancer {
builder = builder.implement( ManagedEntity.class ) builder = builder.implement( ManagedEntity.class )
.defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, Object.class, Visibility.PUBLIC ) .defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, Object.class, Visibility.PUBLIC )
.intercept( FixedValue.self() ); .intercept( FixedValue.self() );
es.enabledInterfaceManagedEntity();
builder = addFieldWithGetterAndSetter( builder = addFieldWithGetterAndSetter(
builder, builder,
@ -212,7 +209,7 @@ public class EnhancerImpl implements Enhancer {
EnhancerConstants.NEXT_SETTER_NAME EnhancerConstants.NEXT_SETTER_NAME
); );
builder = addInterceptorHandling( builder, managedCtClass, es ); builder = addInterceptorHandling( builder, managedCtClass );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) { if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
List<AnnotatedFieldDescription> collectionFields = collectCollectionFields( managedCtClass ); List<AnnotatedFieldDescription> collectionFields = collectCollectionFields( managedCtClass );
@ -235,7 +232,6 @@ public class EnhancerImpl implements Enhancer {
.intercept( implementationSuspendDirtyTracking ) .intercept( implementationSuspendDirtyTracking )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC ) .defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC )
.intercept( implementationGetCollectionTrackerWithoutCollections ); .intercept( implementationGetCollectionTrackerWithoutCollections );
es.enabledInterfaceSelfDirtinessTracker();
} }
else { else {
//TODO es.enableInterfaceExtendedSelfDirtinessTracker ? Careful with consequences.. //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 ) ) { else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() ); log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() );
builder = builder.implement( ManagedComposite.class ); builder = builder.implement( ManagedComposite.class );
builder = addInterceptorHandling( builder, managedCtClass, es ); builder = addInterceptorHandling( builder, managedCtClass );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) { if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
builder = builder.implement( CompositeTracker.class ) builder = builder.implement( CompositeTracker.class )
@ -366,17 +362,17 @@ public class EnhancerImpl implements Enhancer {
.intercept( implementationClearOwner ); .intercept( implementationClearOwner );
} }
return createTransformer( managedCtClass ).applyTo( builder, es ); return createTransformer( managedCtClass ).applyTo( builder );
} }
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() ); log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
builder = builder.implement( ManagedMappedSuperclass.class ); builder = builder.implement( ManagedMappedSuperclass.class );
return createTransformer( managedCtClass ).applyTo( builder, es ); return createTransformer( managedCtClass ).applyTo( builder );
} }
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) { else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() ); log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() );
return createTransformer( managedCtClass ).applyExtended( builder, es ); return createTransformer( managedCtClass ).applyExtended( builder );
} }
else { else {
log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() ); log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() );
@ -398,13 +394,12 @@ public class EnhancerImpl implements Enhancer {
return false; 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 // interceptor handling is only needed if class has lazy-loadable attributes
if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) { if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() ); log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() );
builder = builder.implement( PersistentAttributeInterceptable.class ); builder = builder.implement( PersistentAttributeInterceptable.class );
es.enabledInterfacePersistentAttributeInterceptable();
builder = addFieldWithGetterAndSetter( builder = addFieldWithGetterAndSetter(
builder, 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;
}
}
} }

View File

@ -224,8 +224,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
return null; return null;
} }
DynamicType.Builder<?> applyTo(DynamicType.Builder<?> builder, EnhancerImpl.EnhancementStatus es) { DynamicType.Builder<?> applyTo(DynamicType.Builder<?> builder) {
builder = es.applySuperInterfaceOptimisations(builder);
boolean compositeOwner = false; boolean compositeOwner = false;
// Remove the private modifier from the constructor, which allows to create a better InstantiationOptimizer // 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 ) ) { if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
builder = applyExtended( builder, es ); builder = applyExtended( builder );
} }
return 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 ); AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper enhancer = new FieldAccessEnhancer( managedCtClass, enhancementContext, classPool );
return builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, enhancer ) ); return builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, enhancer ) );
} }

View File

@ -6,86 +6,74 @@
*/ */
package org.hibernate.engine.internal; 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.Managed;
import org.hibernate.engine.spi.ManagedEntity; import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.ManagedMappedSuperclass;
import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SelfDirtinessTracker;
import java.util.Objects;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
* This is a helper to encapsulate an optimal strategy to execute type checks * This is a helper to encapsulate an optimal strategy to execute type checks
* for interfaces which attempts to avoid the performance issues tracked * for interfaces which attempts to avoid the performance issues tracked
* as https://bugs.openjdk.org/browse/JDK-8180450 ; * as <a href="https://bugs.openjdk.org/browse/JDK-8180450">JDK-8180450</a>;
* the problem is complex and best understood by reading the OpenJDK tracker; * the problem is complex and explained better on the OpenJDK tracker;
* we'll focus on a possible solution here. * we'll focus on a possible solution here.
* </p> * <p>
* To avoid polluting the secondary super-type cache, the important aspect is to * 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 * 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) * 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 * 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: * using bytecode enhancement, as they are being frequently checked for compatibility with
* {@see org.hibernate.engine.spi.PersistentAttributeInterceptable} * the following interfaces:
* {@see org.hibernate.engine.spi.ManagedEntity} * <ul>
* {@see org.hibernate.engine.spi.SelfDirtinessTracker} * <li>{@link org.hibernate.engine.spi.PersistentAttributeInterceptable}</li>
* {@see org.hibernate.engine.spi.Managed} * <li>{@link org.hibernate.engine.spi.ManagedEntity}</li>
* With our domain knowledge, we bet on the assumption that either enhancement isn't being * <li>{@link org.hibernate.engine.spi.SelfDirtinessTracker}</li>
* used at all, OR that when enhancement is being used, there is a strong likelyhood for * <li>{@link org.hibernate.engine.spi.Managed}</li>
* all of these supertypes to be have been injected into the managed objected of the domain * <li>{@link org.hibernate.proxy.HibernateProxy}</li>
* model (this isn't a certainty as otherwise we'd not have multiple interfaces to separate * </ul>
* these), but we're working based on the assumption so to at least optimise for what * <p>
* we expect being a very common configuration. * Some additional interfaces are involved in bytecode enhancement (such as {@link ManagedMappedSuperclass}),
* (At this time we won't optimise also embeddables and other corner cases, which will * but some might not be managed here as there was no evidence of them triggering the problem;
* need to be looked at separately). * this might change after further testing.
* We therefore introduce a new marker interface {@see EnhancedEntity}, which extends * <p>
* all these other contracts, and have the enhancer tool apply it when all other interfaces * The approach we pursue is to have all these internal interfaces extend a single
* have been applied. * interface {@link PrimeAmongSecondarySupertypes} which then exposes a type widening
* This then allows to check always and consistently for this type only; as fallback * contract; this allows to consistently cast to {@code PrimeAmongSecondarySupertypes} exclusively
* path, we perform the "traditional" operation as it would have been before this patch. * 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.
* <p>
* This presents two known drawbacks:
* <p>
* 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.
* <p>
* 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 * @author Sanne Grinovero
*/ */
public final class ManagedTypeHelper { public final class ManagedTypeHelper {
private static final ClassValue<TypeMeta> typeMetaCache = new ClassValue<>() {
@Override
protected TypeMeta computeValue(Class<?> type) {
return new TypeMeta(type);
}
};
/** /**
* @param type * @param type
* @return true if and only if the type is assignable to a {@see Managed} type. * @return true if and only if the type is assignable to a {@see Managed} type.
*/ */
public static boolean isManagedType(final Class type) { public static boolean isManagedType(final Class type) {
return EnhancedEntity.class.isAssignableFrom( type ) || Managed.class.isAssignableFrom( type ); return typeMetaCache.get( type ).isManagedType;
}
/**
* @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;
} }
/** /**
@ -93,9 +81,54 @@ public final class ManagedTypeHelper {
* @return true if and only if the type is assignable to a {@see SelfDirtinessTracker} type. * @return true if and only if the type is assignable to a {@see SelfDirtinessTracker} type.
*/ */
public static boolean isSelfDirtinessTrackerType(final Class 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} * Helper to execute an action on an entity, but exclusively if it's implementing the {@see PersistentAttributeInterceptable}
* interface. Otherwise no action is performed. * interface. Otherwise no action is performed.
@ -109,13 +142,12 @@ public final class ManagedTypeHelper {
final Object entity, final Object entity,
final BiConsumer<PersistentAttributeInterceptable, T> action, final BiConsumer<PersistentAttributeInterceptable, T> action,
final T optionalParam) { final T optionalParam) {
if ( entity instanceof EnhancedEntity ) { if ( entity instanceof PrimeAmongSecondarySupertypes ) {
EnhancedEntity e = (EnhancedEntity) entity; final PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity;
action.accept( e, optionalParam ); final PersistentAttributeInterceptable e = t.asPersistentAttributeInterceptable();
} if ( e != null ) {
else if ( entity instanceof PersistentAttributeInterceptable ) { action.accept( e, optionalParam );
PersistentAttributeInterceptable e = (PersistentAttributeInterceptable) entity; }
action.accept( e, optionalParam );
} }
} }
@ -127,13 +159,12 @@ public final class ManagedTypeHelper {
* @param action * @param action
*/ */
public static void processIfSelfDirtinessTracker(final Object entity, final Consumer<SelfDirtinessTracker> action) { public static void processIfSelfDirtinessTracker(final Object entity, final Consumer<SelfDirtinessTracker> action) {
if ( entity instanceof EnhancedEntity ) { if ( entity instanceof PrimeAmongSecondarySupertypes ) {
EnhancedEntity e = (EnhancedEntity) entity; final PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity;
action.accept( e ); final SelfDirtinessTracker e = t.asSelfDirtinessTracker();
} if ( e != null ) {
else if ( entity instanceof SelfDirtinessTracker ) { action.accept( e );
SelfDirtinessTracker e = (SelfDirtinessTracker) entity; }
action.accept( e );
} }
} }
@ -151,13 +182,12 @@ public final class ManagedTypeHelper {
final Object entity, final Object entity,
final BiConsumer<SelfDirtinessTracker, T> action, final BiConsumer<SelfDirtinessTracker, T> action,
final T optionalParam) { final T optionalParam) {
if ( entity instanceof EnhancedEntity ) { if ( entity instanceof PrimeAmongSecondarySupertypes ) {
EnhancedEntity e = (EnhancedEntity) entity; final PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity;
action.accept( e, optionalParam ); final SelfDirtinessTracker e = t.asSelfDirtinessTracker();
} if ( e != null ) {
else if ( entity instanceof SelfDirtinessTracker ) { action.accept( e, optionalParam );
SelfDirtinessTracker e = (SelfDirtinessTracker) entity; }
action.accept( e, optionalParam );
} }
} }
@ -169,12 +199,34 @@ public final class ManagedTypeHelper {
* @throws ClassCastException if it's not of the right type * @throws ClassCastException if it's not of the right type
*/ */
public static PersistentAttributeInterceptable asPersistentAttributeInterceptable(final Object entity) { public static PersistentAttributeInterceptable asPersistentAttributeInterceptable(final Object entity) {
if ( entity instanceof EnhancedEntity ) { Objects.requireNonNull( entity );
return (EnhancedEntity) entity; if ( entity instanceof PrimeAmongSecondarySupertypes ) {
PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity;
final PersistentAttributeInterceptable e = t.asPersistentAttributeInterceptable();
if ( e != null ) {
return e;
}
} }
else { throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to PersistentAttributeInterceptable" );
return (PersistentAttributeInterceptable) entity; }
/**
* 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 * @throws ClassCastException if it's not of the right type
*/ */
public static ManagedEntity asManagedEntity(final Object entity) { public static ManagedEntity asManagedEntity(final Object entity) {
if ( entity instanceof EnhancedEntity ) { Objects.requireNonNull( entity );
return (EnhancedEntity) entity; if ( entity instanceof PrimeAmongSecondarySupertypes ) {
} PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity;
else { final ManagedEntity e = t.asManagedEntity();
return (ManagedEntity) entity; 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 * @throws ClassCastException if it's not of the right type
*/ */
public static SelfDirtinessTracker asSelfDirtinessTracker(final Object entity) { public static SelfDirtinessTracker asSelfDirtinessTracker(final Object entity) {
if ( entity instanceof EnhancedEntity ) { Objects.requireNonNull( entity );
return (EnhancedEntity) entity; if ( entity instanceof PrimeAmongSecondarySupertypes ) {
PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity;
final SelfDirtinessTracker e = t.asSelfDirtinessTracker();
if ( e != null ) {
return e;
}
} }
else { throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to SelfDirtinessTracker" );
return (SelfDirtinessTracker) entity; }
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 );
} }
} }

View File

@ -265,7 +265,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
} }
private static void processEntityOnClear(final Object entity) { 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 ); ManagedTypeHelper.processIfPersistentAttributeInterceptable( entity, StatefulPersistenceContext::unsetSession, null );
} }

View File

@ -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
}

View File

@ -22,5 +22,16 @@ package org.hibernate.engine.spi;
* *
* @author Steve Ebersole * @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;
}
} }

View File

@ -12,4 +12,15 @@ package org.hibernate.engine.spi;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ManagedComposite extends Managed { public interface ManagedComposite extends Managed {
/**
* Special internal contract to optimize type checking
* @see PrimeAmongSecondarySupertypes
* @return this same instance
*/
@Override
default ManagedComposite asManagedComposite() {
return this;
}
} }

View File

@ -79,4 +79,15 @@ public interface ManagedEntity extends Managed {
* @param next The next entry * @param next The next entry
*/ */
void $$_hibernate_setNextManagedEntity(ManagedEntity next); void $$_hibernate_setNextManagedEntity(ManagedEntity next);
/**
* Special internal contract to optimize type checking
* @see PrimeAmongSecondarySupertypes
* @return this same instance
*/
@Override
default ManagedEntity asManagedEntity() {
return this;
}
} }

View File

@ -12,4 +12,15 @@ package org.hibernate.engine.spi;
* @author Luis Barreiro * @author Luis Barreiro
*/ */
public interface ManagedMappedSuperclass extends Managed { public interface ManagedMappedSuperclass extends Managed {
/**
* Special internal contract to optimize type checking
* @see PrimeAmongSecondarySupertypes
* @return this same instance
*/
@Override
default ManagedMappedSuperclass asManagedMappedSuperclass() {
return this;
}
} }

View File

@ -9,7 +9,18 @@ package org.hibernate.engine.spi;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface PersistentAttributeInterceptable { public interface PersistentAttributeInterceptable extends PrimeAmongSecondarySupertypes {
PersistentAttributeInterceptor $$_hibernate_getInterceptor(); PersistentAttributeInterceptor $$_hibernate_getInterceptor();
void $$_hibernate_setInterceptor(PersistentAttributeInterceptor interceptor); void $$_hibernate_setInterceptor(PersistentAttributeInterceptor interceptor);
/**
* Special internal contract to optimize type checking
* @see PrimeAmongSecondarySupertypes
* @return this same instance
*/
@Override
default PersistentAttributeInterceptable asPersistentAttributeInterceptable() {
return this;
}
} }

View File

@ -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;
}
}

View File

@ -18,7 +18,7 @@ import org.hibernate.bytecode.enhance.spi.CollectionTracker;
* *
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a> * @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/ */
public interface SelfDirtinessTracker { public interface SelfDirtinessTracker extends PrimeAmongSecondarySupertypes {
/** /**
* Have any of the entity's persistent attributes changed? * Have any of the entity's persistent attributes changed?
* *
@ -53,4 +53,15 @@ public interface SelfDirtinessTracker {
* Get access to the CollectionTracker * Get access to the CollectionTracker
*/ */
CollectionTracker $$_hibernate_getCollectionTracker(); CollectionTracker $$_hibernate_getCollectionTracker();
/**
* Special internal contract to optimize type checking
* @see PrimeAmongSecondarySupertypes
* @return this same instance
*/
@Override
default SelfDirtinessTracker asSelfDirtinessTracker() {
return this;
}
} }

View File

@ -38,7 +38,7 @@ public class PropertyAccessStrategyResolverStandardImpl implements PropertyAcces
if ( BuiltInPropertyAccessStrategies.BASIC.getExternalName().equals( explicitAccessStrategyName ) if ( BuiltInPropertyAccessStrategies.BASIC.getExternalName().equals( explicitAccessStrategyName )
|| BuiltInPropertyAccessStrategies.FIELD.getExternalName().equals( explicitAccessStrategyName ) || BuiltInPropertyAccessStrategies.FIELD.getExternalName().equals( explicitAccessStrategyName )
|| BuiltInPropertyAccessStrategies.MIXED.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 ) ) { if ( isManagedType( containerClass ) ) {
// PROPERTY (BASIC) and MIXED are not valid for bytecode enhanced entities... // PROPERTY (BASIC) and MIXED are not valid for bytecode enhanced entities...
return PropertyAccessStrategyEnhancedImpl.INSTANCE; return PropertyAccessStrategyEnhancedImpl.INSTANCE;