HHH-15790 New dispatch approach to manage type checks of enhanced entities
This commit is contained in:
parent
c595347803
commit
3028299b4a
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue