HHH-15616 Mitigate performance impact of entity enhancement on Klass's _secondary_super_cache

This commit is contained in:
Sanne Grinovero 2022-10-13 11:03:32 +01:00 committed by Christian Beikov
parent 16c39c0925
commit 53076f3029
17 changed files with 372 additions and 68 deletions

View File

@ -28,6 +28,7 @@ import org.hibernate.collection.spi.PersistentSet;
import org.hibernate.collection.spi.PersistentSortedMap;
import org.hibernate.collection.spi.PersistentSortedSet;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -108,8 +109,10 @@ public final class Hibernate {
if ( proxy instanceof HibernateProxy ) {
return !( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUninitialized();
}
else if ( proxy instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) proxy ).$$_hibernate_getInterceptor();
else if ( ManagedTypeHelper.isPersistentAttributeInterceptable( proxy ) ) {
final PersistentAttributeInterceptable asPersistentAttributeInterceptable = ManagedTypeHelper.asPersistentAttributeInterceptable(
proxy );
final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable.$$_hibernate_getInterceptor();
return !(interceptor instanceof EnhancementAsProxyLazinessInterceptor);
}
else if ( proxy instanceof LazyInitializable ) {

View File

@ -36,6 +36,7 @@ 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;
@ -176,6 +177,7 @@ 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 );
@ -184,6 +186,7 @@ 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,
@ -207,7 +210,7 @@ public class EnhancerImpl implements Enhancer {
EnhancerConstants.NEXT_SETTER_NAME
);
builder = addInterceptorHandling( builder, managedCtClass );
builder = addInterceptorHandling( builder, managedCtClass, es );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
List<AnnotatedFieldDescription> collectionFields = collectCollectionFields( managedCtClass );
@ -230,8 +233,10 @@ 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..
builder = builder.implement( ExtendedSelfDirtinessTracker.class )
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
.annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
@ -326,13 +331,13 @@ public class EnhancerImpl implements Enhancer {
}
}
return createTransformer( managedCtClass ).applyTo( builder );
return createTransformer( managedCtClass ).applyTo( builder, es );
}
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() );
builder = builder.implement( ManagedComposite.class );
builder = addInterceptorHandling( builder, managedCtClass );
builder = addInterceptorHandling( builder, managedCtClass, es );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
builder = builder.implement( CompositeTracker.class )
@ -359,17 +364,17 @@ public class EnhancerImpl implements Enhancer {
.intercept( implementationClearOwner );
}
return createTransformer( managedCtClass ).applyTo( builder );
return createTransformer( managedCtClass ).applyTo( builder, es );
}
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
builder = builder.implement( ManagedMappedSuperclass.class );
return createTransformer( managedCtClass ).applyTo( builder );
return createTransformer( managedCtClass ).applyTo( builder, es );
}
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() );
return createTransformer( managedCtClass ).applyExtended( builder );
return createTransformer( managedCtClass ).applyExtended( builder, es );
}
else {
log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() );
@ -391,12 +396,13 @@ public class EnhancerImpl implements Enhancer {
return false;
}
private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> builder, TypeDescription managedCtClass) {
private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> builder, TypeDescription managedCtClass, EnhancementStatus es) {
// 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,
@ -616,4 +622,53 @@ public class EnhancerImpl implements Enhancer {
this.resolution = new Resolution.Explicit( bytes);
}
}
/**
* 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

@ -207,7 +207,8 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
return null;
}
DynamicType.Builder<?> applyTo(DynamicType.Builder<?> builder) {
DynamicType.Builder<?> applyTo(DynamicType.Builder<?> builder, EnhancerImpl.EnhancementStatus es) {
builder = es.applySuperInterfaceOptimisations(builder);
boolean compositeOwner = false;
builder = builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, this ) );
@ -262,7 +263,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
}
if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
builder = applyExtended( builder );
builder = applyExtended( builder, es );
}
return builder;
@ -311,7 +312,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla
}
}
DynamicType.Builder<?> applyExtended(DynamicType.Builder<?> builder) {
DynamicType.Builder<?> applyExtended(DynamicType.Builder<?> builder, EnhancerImpl.EnhancementStatus es) {
AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper enhancer = new FieldAccessEnhancer( managedCtClass, enhancementContext, classPool );
return builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, enhancer ) );
}

View File

@ -15,6 +15,7 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.bytecode.BytecodeLogging;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -72,7 +73,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
}
}
this.inLineDirtyChecking = SelfDirtinessTracker.class.isAssignableFrom( entityPersister.getMappedClass() );
this.inLineDirtyChecking = ManagedTypeHelper.isSelfDirtinessTrackerType( entityPersister.getMappedClass() );
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to initialise the entity
// because the pre-computed update statement contains even not dirty properties and so we need all the values
// we have to initialise it even if it's versioned to fetch the current version

View File

@ -21,6 +21,7 @@ import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryExtraState;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
@ -259,9 +260,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
getPersister().setValue( entity, getPersister().getVersionProperty(), nextVersion );
}
if( entity instanceof SelfDirtinessTracker ) {
( (SelfDirtinessTracker) entity ).$$_hibernate_clearDirtyAttributes();
}
ManagedTypeHelper.processIfSelfDirtinessTracker( entity, AbstractEntityEntry::clearDirtyAttributes );
getPersistenceContext().getSession()
.getFactory()
@ -269,6 +268,10 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
.resetDirty( entity, getPersister(), (Session) getPersistenceContext().getSession() );
}
private static void clearDirtyAttributes(final SelfDirtinessTracker entity) {
entity.$$_hibernate_clearDirtyAttributes();
}
@Override
public void postDelete() {
setCompressedValue( EnumState.PREVIOUS_STATUS, getStatus() );
@ -325,10 +328,10 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
@SuppressWarnings( {"SimplifiableIfStatement"})
private boolean isUnequivocallyNonDirty(Object entity) {
if ( entity instanceof SelfDirtinessTracker ) {
if ( ManagedTypeHelper.isSelfDirtinessTracker( entity ) ) {
boolean uninitializedProxy = false;
if ( entity instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) entity;
if ( ManagedTypeHelper.isPersistentAttributeInterceptable( entity ) ) {
final PersistentAttributeInterceptable interceptable = ManagedTypeHelper.asPersistentAttributeInterceptable( entity );
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
EnhancementAsProxyLazinessInterceptor enhancementAsProxyLazinessInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
@ -342,11 +345,11 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
// we never have to check an uninitialized proxy
return uninitializedProxy || !persister.hasCollections()
&& !persister.hasMutableProperties()
&& !( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes();
&& !ManagedTypeHelper.asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
}
if ( entity instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) entity;
if ( ManagedTypeHelper.isPersistentAttributeInterceptable( entity ) ) {
final PersistentAttributeInterceptable interceptable = ManagedTypeHelper.asPersistentAttributeInterceptable( entity );
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
// we never have to check an uninitialized proxy

View File

@ -9,6 +9,7 @@ package org.hibernate.engine.internal;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistenceContext;
@ -90,21 +91,22 @@ public class EntityEntryContext {
ManagedEntity managedEntity = getAssociatedManagedEntity( entity );
final boolean alreadyAssociated = managedEntity != null;
if ( !alreadyAssociated ) {
if (entity instanceof ManagedEntity) {
if ( ManagedTypeHelper.isManaged( entity ) ) {
final ManagedEntity managed = ManagedTypeHelper.asManagedEntity( entity );
if ( entityEntry.getPersister().isMutable() ) {
managedEntity = (ManagedEntity) entity;
managedEntity = managed;
// We know that managedEntity is not associated with the same PersistenceContext.
// Check if managedEntity is associated with a different PersistenceContext.
checkNotAssociatedWithOtherPersistenceContextIfMutable( managedEntity );
}
else {
// Create a holder for PersistenceContext-related data.
managedEntity = new ImmutableManagedEntityHolder( (ManagedEntity) entity );
managedEntity = new ImmutableManagedEntityHolder( managed );
if ( immutableManagedEntityXref == null ) {
immutableManagedEntityXref = new IdentityHashMap<>();
}
immutableManagedEntityXref.put(
(ManagedEntity) entity,
managed,
(ImmutableManagedEntityHolder) managedEntity
);
}
@ -149,8 +151,8 @@ public class EntityEntryContext {
}
private ManagedEntity getAssociatedManagedEntity(Object entity) {
if (entity instanceof ManagedEntity) {
final ManagedEntity managedEntity = (ManagedEntity) entity;
if ( ManagedTypeHelper.isManaged( entity ) ) {
final ManagedEntity managedEntity = ManagedTypeHelper.asManagedEntity( entity );
if ( managedEntity.$$_hibernate_getEntityEntry() == null ) {
// it is not associated
return null;

View File

@ -0,0 +1,187 @@
/*
* 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.internal;
import org.hibernate.engine.spi.EnhancedEntity;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.SelfDirtinessTracker;
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;
* we'll focus on a possible solution here.
* </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.
* @author Sanne Grinovero
*/
public final class ManagedTypeHelper {
/**
* @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 Managed}
*/
public static boolean isManaged(final Object entity) {
return entity instanceof EnhancedEntity || entity instanceof Managed;
}
/**
* @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;
}
/**
* @param type
* @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 );
}
/**
* Helper to execute an action on an entity, but exclusively if it's implementing the {@see PersistentAttributeInterceptable}
* interface. Otherwise no action is performed.
*
* @param entity
* @param action The action to be performed; it should take the entity as first parameter, and an additional parameter T as second parameter.
* @param optionalParam a parameter which can be passed to the action
* @param <T> the type of the additional parameter.
*/
public static <T> void processIfPersistentAttributeInterceptable(
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 the entity is implementing SelfDirtinessTracker, apply some action to it.
* It is first cast to SelfDirtinessTracker using an optimal strategy.
* If the entity does not implement SelfDirtinessTracker, no operation is performed.
* @param entity
* @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 );
}
}
/**
* Cast the object to PersistentAttributeInterceptable
* (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 PersistentAttributeInterceptable asPersistentAttributeInterceptable(final Object entity) {
if ( entity instanceof EnhancedEntity ) {
return (EnhancedEntity) entity;
}
else {
return (PersistentAttributeInterceptable) entity;
}
}
/**
* Cast the object to ManagedEntity
* (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 ManagedEntity asManagedEntity(final Object entity) {
if ( entity instanceof EnhancedEntity ) {
return (EnhancedEntity) entity;
}
else {
return (ManagedEntity) entity;
}
}
/**
* Cast the object to SelfDirtinessTracker
* (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 SelfDirtinessTracker asSelfDirtinessTracker(final Object entity) {
if ( entity instanceof EnhancedEntity ) {
return (EnhancedEntity) entity;
}
else {
return (SelfDirtinessTracker) entity;
}
}
}

View File

@ -37,6 +37,7 @@ import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInter
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.AssociationKey;
import org.hibernate.engine.spi.BatchFetchQueue;
import org.hibernate.engine.spi.CollectionEntry;
@ -229,13 +230,9 @@ public class StatefulPersistenceContext implements PersistenceContext {
} );
}
for ( Entry<Object, EntityEntry> objectEntityEntryEntry : entityEntryContext.reentrantSafeEntityEntries() ) {
if ( objectEntityEntryEntry.getKey() instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) objectEntityEntryEntry.getKey() ).$$_hibernate_getInterceptor();
if ( interceptor instanceof LazyAttributeLoadingInterceptor ) {
( (LazyAttributeLoadingInterceptor) interceptor ).unsetSession();
}
}
for ( Entry<Object, EntityEntry> objectEntityEntryEntry : entityEntryContext.reentrantSafeEntityEntries() ) {//TODO make this a forEach process within the container
//type-cache-pollution agent: always check for EnhancedEntity type first.
ManagedTypeHelper.processIfPersistentAttributeInterceptable( objectEntityEntryEntry.getKey(), StatefulPersistenceContext::unsetSession, null );
}
final SharedSessionContractImplementor session = getSession();
@ -267,6 +264,13 @@ public class StatefulPersistenceContext implements PersistenceContext {
naturalIdResolutions = null;
}
private static void unsetSession(PersistentAttributeInterceptable persistentAttributeInterceptable, Object ignoredParam) {
final PersistentAttributeInterceptor interceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof LazyAttributeLoadingInterceptor ) {
( (LazyAttributeLoadingInterceptor) interceptor ).unsetSession();
}
}
@Override
public boolean isDefaultReadOnly() {
return defaultReadOnly;
@ -670,8 +674,8 @@ public class StatefulPersistenceContext implements PersistenceContext {
//initialize + unwrap the object and return it
return li.getImplementation();
}
else if ( maybeProxy instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) maybeProxy;
else if ( ManagedTypeHelper.isPersistentAttributeInterceptable( maybeProxy ) ) {
final PersistentAttributeInterceptable interceptable = ManagedTypeHelper.asPersistentAttributeInterceptable( maybeProxy );
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( maybeProxy, null );

View File

@ -0,0 +1,28 @@
/*
* 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

@ -15,9 +15,12 @@ import org.hibernate.StaleObjectStateException;
import org.hibernate.action.internal.DelayedPostInsertIdentifier;
import org.hibernate.action.internal.EntityUpdateAction;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.internal.Nullability;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
@ -99,9 +102,10 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
}
private static boolean isUninitializedEnhanced(Object entity) {
if ( entity instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor =
( (PersistentAttributeInterceptable) entity).$$_hibernate_getInterceptor();
if ( ManagedTypeHelper.isPersistentAttributeInterceptable( entity ) ) {
final PersistentAttributeInterceptable asPersistentAttributeInterceptable = ManagedTypeHelper.asPersistentAttributeInterceptable(
entity );
final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable.$$_hibernate_getInterceptor();
// the entity is an un-initialized enhancement-as-proxy reference
return interceptor instanceof EnhancementAsProxyLazinessInterceptor;
}

View File

@ -8,9 +8,9 @@ package org.hibernate.metamodel.internal;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.spi.EntityInstantiator;
import org.hibernate.tuple.entity.EntityMetamodel;
@ -35,7 +35,8 @@ public abstract class AbstractEntityInstantiatorPojo extends AbstractPojoInstant
this.entityMetamodel = entityMetamodel;
this.proxyInterface = persistentClass.getProxyInterface();
this.applyBytecodeInterception = PersistentAttributeInterceptable.class.isAssignableFrom( persistentClass.getMappedClass() );
//TODO this PojoEntityInstantiator appears to not be reused ?!
this.applyBytecodeInterception = ManagedTypeHelper.isPersistentAttributeInterceptableType( persistentClass.getMappedClass() );
}
protected Object applyInterception(Object entity) {
@ -51,7 +52,7 @@ public abstract class AbstractEntityInstantiatorPojo extends AbstractPojoInstant
.getLazyAttributeNames(),
null
);
( (PersistentAttributeInterceptable) entity ).$$_hibernate_setInterceptor( interceptor );
ManagedTypeHelper.asPersistentAttributeInterceptable( entity ).$$_hibernate_setInterceptor( interceptor );
return entity;
}

View File

@ -12,6 +12,7 @@ import java.lang.reflect.Constructor;
import org.hibernate.InstantiationException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -47,7 +48,7 @@ public class EntityInstantiatorPojoStandard extends AbstractEntityInstantiatorPo
? null
: resolveConstructor( getMappedPojoClass() );
this.applyBytecodeInterception = PersistentAttributeInterceptable.class.isAssignableFrom( persistentClass.getMappedClass() );
this.applyBytecodeInterception = ManagedTypeHelper.isPersistentAttributeInterceptableType( persistentClass.getMappedClass() );
}
protected static Constructor<?> resolveConstructor(Class<?> mappedPojoClass) {
@ -80,7 +81,7 @@ public class EntityInstantiatorPojoStandard extends AbstractEntityInstantiatorPo
.getLazyAttributeNames(),
null
);
( (PersistentAttributeInterceptable) entity ).$$_hibernate_setInterceptor( interceptor );
ManagedTypeHelper.asPersistentAttributeInterceptable( entity ).$$_hibernate_setInterceptor( interceptor );
return entity;
}

View File

@ -23,6 +23,7 @@ import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.bytecode.spi.ReflectionOptimizer.InstantiationOptimizer;
import org.hibernate.cfg.Environment;
import org.hibernate.classic.Lifecycle;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
@ -99,8 +100,7 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent
}
this.lifecycleImplementor = Lifecycle.class.isAssignableFrom( mappedJavaType );
this.isBytecodeEnhanced = PersistentAttributeInterceptable.class.isAssignableFrom( mappedJavaType );
this.isBytecodeEnhanced = ManagedTypeHelper.isPersistentAttributeInterceptableType( mappedJavaType );
final Property identifierProperty = bootDescriptor.getIdentifierProperty();
if ( identifierProperty == null ) {

View File

@ -71,6 +71,7 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.internal.CacheHelper;
import org.hibernate.engine.internal.ImmutableEntityEntryFactory;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.internal.MutableEntityEntryFactory;
import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.internal.Versioning;
@ -85,6 +86,7 @@ import org.hibernate.engine.spi.EntityEntryFactory;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.NaturalIdResolutions;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
@ -4993,7 +4995,7 @@ public abstract class AbstractEntityPersister
@Override
public void afterInitialize(Object entity, SharedSessionContractImplementor session) {
if ( entity instanceof PersistentAttributeInterceptable && getRepresentationStrategy().getMode() == RepresentationMode.POJO ) {
if ( ManagedTypeHelper.isPersistentAttributeInterceptable( entity ) && getRepresentationStrategy().getMode() == RepresentationMode.POJO ) {
final BytecodeLazyAttributeInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata()
.extractLazyInterceptor( entity );
assert interceptor != null;
@ -5003,9 +5005,11 @@ public abstract class AbstractEntityPersister
}
// clear the fields that are marked as dirty in the dirtiness tracker
if ( entity instanceof SelfDirtinessTracker ) {
( (SelfDirtinessTracker) entity ).$$_hibernate_clearDirtyAttributes();
}
ManagedTypeHelper.processIfSelfDirtinessTracker( entity, AbstractEntityPersister::clearDirtyAttributes );
}
private static void clearDirtyAttributes(final SelfDirtinessTracker entity) {
entity.$$_hibernate_clearDirtyAttributes();
}
@Override
@ -5247,11 +5251,14 @@ public abstract class AbstractEntityPersister
if ( session == null ) {
return;
}
if ( entity instanceof PersistentAttributeInterceptable ) {
final BytecodeLazyAttributeInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata().extractLazyInterceptor( entity );
if ( interceptor != null ) {
interceptor.setSession( session );
}
ManagedTypeHelper.processIfPersistentAttributeInterceptable( entity, this::setSession, session );
}
private void setSession(PersistentAttributeInterceptable entity, SharedSessionContractImplementor session) {
final BytecodeLazyAttributeInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata()
.extractLazyInterceptor( entity );
if ( interceptor != null ) {
interceptor.setSession( session );
}
}

View File

@ -8,7 +8,7 @@ package org.hibernate.property.access.internal;
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies;
@ -37,7 +37,8 @@ public class PropertyAccessStrategyResolverStandardImpl implements PropertyAcces
if ( BuiltInPropertyAccessStrategies.BASIC.getExternalName().equals( explicitAccessStrategyName )
|| BuiltInPropertyAccessStrategies.FIELD.getExternalName().equals( explicitAccessStrategyName )
|| BuiltInPropertyAccessStrategies.MIXED.getExternalName().equals( explicitAccessStrategyName ) ) {
if ( Managed.class.isAssignableFrom( containerClass ) ) {
//type-cache-pollution agent: always check for EnhancedEntity type first.
if ( ManagedTypeHelper.isManagedType( containerClass ) ) {
// PROPERTY (BASIC) and MIXED are not valid for bytecode enhanced entities...
return PropertyAccessStrategyEnhancedImpl.INSTANCE;
}

View File

@ -10,6 +10,7 @@ import java.io.Serializable;
import java.lang.reflect.Field;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
@ -39,7 +40,7 @@ public class EnhancedSetterImpl extends SetterFieldImpl {
this.propertyName = propertyName;
this.enhancementState = ( CompositeOwner.class.isAssignableFrom( containerClass ) ? COMPOSITE_OWNER : 0 )
| ( CompositeTracker.class.isAssignableFrom( field.getType() ) ? COMPOSITE_TRACKER_MASK : 0 )
| ( PersistentAttributeInterceptable.class.isAssignableFrom( containerClass ) ? PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK : 0 );
| ( ManagedTypeHelper.isPersistentAttributeInterceptableType( containerClass ) ? PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK : 0 );
}
@Override
@ -53,7 +54,9 @@ public class EnhancedSetterImpl extends SetterFieldImpl {
// This marks the attribute as initialized, so it doesn't get lazily loaded afterwards
if ( ( enhancementState & PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK ) != 0 ) {
PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) target ).$$_hibernate_getInterceptor();
final PersistentAttributeInterceptable asPersistentAttributeInterceptable = ManagedTypeHelper.asPersistentAttributeInterceptable(
target );
PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof BytecodeLazyAttributeInterceptor ) {
( (BytecodeLazyAttributeInterceptor) interceptor ).attributeInitialized( propertyName );
}

View File

@ -16,6 +16,7 @@ import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterc
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.bytecode.spi.NotInstrumentedException;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
@ -41,7 +42,7 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
boolean collectionsInDefaultFetchGroupEnabled,
Metadata metadata) {
final Class<?> mappedClass = persistentClass.getMappedClass();
final boolean enhancedForLazyLoading = PersistentAttributeInterceptable.class.isAssignableFrom( mappedClass );
final boolean enhancedForLazyLoading = ManagedTypeHelper.isPersistentAttributeInterceptableType( mappedClass );
final LazyAttributesMetadata lazyAttributesMetadata = enhancedForLazyLoading
? LazyAttributesMetadata.from( persistentClass, true, collectionsInDefaultFetchGroupEnabled, metadata )
: LazyAttributesMetadata.nonEnhanced( persistentClass.getEntityName() );
@ -141,15 +142,13 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
final PersistenceContext persistenceContext = session.getPersistenceContext();
// first, instantiate the entity instance to use as the proxy
final PersistentAttributeInterceptable entity = (PersistentAttributeInterceptable) persister.instantiate(
final PersistentAttributeInterceptable entity = ManagedTypeHelper.asPersistentAttributeInterceptable( persister.instantiate(
identifier,
session
);
) );
// clear the fields that are marked as dirty in the dirtiness tracker
if ( entity instanceof SelfDirtinessTracker ) {
( (SelfDirtinessTracker) entity ).$$_hibernate_clearDirtyAttributes();
}
ManagedTypeHelper.processIfSelfDirtinessTracker( entity, BytecodeEnhancementMetadataPojoImpl::clearDirtyAttributes );
// add the entity (proxy) instance to the PC
persistenceContext.addEnhancedProxy( entityKey, entity );
@ -181,6 +180,10 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
return entity;
}
private static void clearDirtyAttributes(final SelfDirtinessTracker entity) {
entity.$$_hibernate_clearDirtyAttributes();
}
@Override
public LazyAttributeLoadingInterceptor injectInterceptor(
Object entity,
@ -248,7 +251,7 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
);
}
( (PersistentAttributeInterceptable) entity ).$$_hibernate_setInterceptor( interceptor );
ManagedTypeHelper.asPersistentAttributeInterceptable( entity ).$$_hibernate_setInterceptor( interceptor );
}
@Override
@ -267,7 +270,7 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
);
}
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
final PersistentAttributeInterceptor interceptor = ManagedTypeHelper.asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
if ( interceptor == null ) {
return null;
}