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.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<AnnotatedFieldDescription> 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;
}
}
}

View File

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

View File

@ -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 <a href="https://bugs.openjdk.org/browse/JDK-8180450">JDK-8180450</a>;
* the problem is complex and explained better on the OpenJDK tracker;
* we'll focus on a possible solution here.
* </p>
* <p>
* 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:
* <ul>
* <li>{@link org.hibernate.engine.spi.PersistentAttributeInterceptable}</li>
* <li>{@link org.hibernate.engine.spi.ManagedEntity}</li>
* <li>{@link org.hibernate.engine.spi.SelfDirtinessTracker}</li>
* <li>{@link org.hibernate.engine.spi.Managed}</li>
* <li>{@link org.hibernate.proxy.HibernateProxy}</li>
* </ul>
* <p>
* 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.
* <p>
* 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.
* <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
*/
public final class ManagedTypeHelper {
private static final ClassValue<TypeMeta> 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<PersistentAttributeInterceptable, T> 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<SelfDirtinessTracker> 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<SelfDirtinessTracker, T> 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 );
}
}

View File

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

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
*/
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
*/
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
*/
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
*/
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
*/
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;
}
}

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>
*/
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;
}
}

View File

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