From d0e7fab351eea4f9271d6945816abe595ad2b908 Mon Sep 17 00:00:00 2001 From: barreiro Date: Tue, 25 Jul 2017 02:45:00 +0100 Subject: [PATCH] HHH-11820 - Simplify dirty tracking on entities without collections [bytebuddy] --- .../internal/bytebuddy/CodeTemplates.java | 38 ++++++- .../internal/bytebuddy/EnhancerImpl.java | 105 +++++++++++------- 2 files changed, 101 insertions(+), 42 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java index 8bfc61d5da..996d7b3bef 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java @@ -14,6 +14,7 @@ import java.util.Map; import org.hibernate.Hibernate; import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker; import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker; +import org.hibernate.bytecode.enhance.internal.tracker.NoopCollectionTracker; import org.hibernate.bytecode.enhance.internal.tracker.SimpleCollectionTracker; import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; import org.hibernate.bytecode.enhance.spi.CollectionTracker; @@ -83,7 +84,23 @@ class CodeTemplates { } } - static class AreCollectionFieldsDirty { + static class GetDirtyAttributesWithoutCollections { + @Advice.OnMethodExit + static void $$_hibernate_getDirtyAttributes( + @Advice.Return(readOnly = false) String[] returned, + @Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME) DirtyTracker $$_hibernate_tracker) { + returned = $$_hibernate_tracker == null ? new String[0] : $$_hibernate_tracker.get(); + } + } + + static class GetCollectionTrackerWithoutCollections { + @Advice.OnMethodExit + static void $$_hibernate_getCollectionTracker( @Advice.Return(readOnly = false) CollectionTracker returned) { + returned = NoopCollectionTracker.INSTANCE; + } + } + + static class AreFieldsDirty { @Advice.OnMethodExit static void $$_hibernate_hasDirtyAttributes( @Advice.This ExtendedSelfDirtinessTracker self, @@ -93,6 +110,15 @@ class CodeTemplates { } } + static class AreFieldsDirtyWithoutCollections { + @Advice.OnMethodExit + static void $$_hibernate_hasDirtyAttributes( + @Advice.Return(readOnly = false) boolean returned, + @Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME) DirtyTracker $$_hibernate_tracker) { + returned = $$_hibernate_tracker != null && !$$_hibernate_tracker.isEmpty(); + } + } + static class ClearDirtyAttributes { @Advice.OnMethodEnter static void $$_hibernate_clearDirtyAttributes( @@ -105,6 +131,16 @@ class CodeTemplates { } } + static class ClearDirtyAttributesWithoutCollections { + @Advice.OnMethodEnter + static void $$_hibernate_clearDirtyAttributes( + @Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME) DirtyTracker $$_hibernate_tracker) { + if ( $$_hibernate_tracker != null ) { + $$_hibernate_tracker.clear(); + } + } + } + static class SuspendDirtyTracking { @Advice.OnMethodEnter static void $$_hibernate_suspendDirtyTracking( diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java index 43ef42992c..ef980e3f0c 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java @@ -35,6 +35,7 @@ import org.hibernate.engine.spi.ManagedEntity; import org.hibernate.engine.spi.ManagedMappedSuperclass; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; +import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; @@ -58,6 +59,7 @@ import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.StubMethod; import net.bytebuddy.pool.TypePool; +import static net.bytebuddy.matcher.ElementMatchers.isDefaultFinalizer; import static net.bytebuddy.matcher.ElementMatchers.isGetter; public class EnhancerImpl implements Enhancer { @@ -95,7 +97,7 @@ public class EnhancerImpl implements Enhancer { try { final TypeDescription managedCtClass = classPool.describe( className ).resolve(); DynamicType.Builder builder = doEnhance( - new ByteBuddy().with( TypeValidation.DISABLED ).redefine( managedCtClass, ClassFileLocator.Simple.of( className, originalBytes ) ), + new ByteBuddy().ignore( isDefaultFinalizer() ).with( TypeValidation.DISABLED ).redefine( managedCtClass, ClassFileLocator.Simple.of( className, originalBytes ) ), managedCtClass ); if ( builder == null ) { @@ -161,29 +163,48 @@ public class EnhancerImpl implements Enhancer { builder = addInterceptorHandling( builder, managedCtClass ); if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) { - builder = builder.implement( ExtendedSelfDirtinessTracker.class ) - .defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldManifestation.TRANSIENT, Visibility.PRIVATE ) - .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() ) - .defineField( EnhancerConstants.TRACKER_COLLECTION_NAME, CollectionTracker.class, FieldManifestation.TRANSIENT, Visibility.PRIVATE ) - .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() ) - .defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC ) - .withParameters( String.class ) - .intercept( Advice.to( CodeTemplates.TrackChange.class ).wrap( StubMethod.INSTANCE ) ) - .defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC ) - .intercept( Advice.to( CodeTemplates.GetDirtyAttributes.class ).wrap( StubMethod.INSTANCE ) ) - .defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC ) - .intercept( Advice.to( CodeTemplates.AreCollectionFieldsDirty.class ).wrap( StubMethod.INSTANCE ) ) - .defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC ) - .intercept( Advice.to( CodeTemplates.ClearDirtyAttributes.class ).wrap( StubMethod.INSTANCE ) ) - .defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC ) - .withParameters( boolean.class ) - .intercept( Advice.to( CodeTemplates.SuspendDirtyTracking.class ).wrap( StubMethod.INSTANCE ) ) - .defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC ) - .intercept( FieldAccessor.ofField( EnhancerConstants.TRACKER_COLLECTION_NAME ) ); + if ( collectCollectionFields( managedCtClass ).isEmpty() ) { + builder = builder.implement( SelfDirtinessTracker.class ) + .defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldManifestation.TRANSIENT, Visibility.PRIVATE ) + .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() ) + .defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC ) + .withParameters( String.class ) + .intercept( Advice.to( CodeTemplates.TrackChange.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC ) + .intercept( Advice.to( CodeTemplates.GetDirtyAttributesWithoutCollections.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC ) + .intercept( Advice.to( CodeTemplates.AreFieldsDirtyWithoutCollections.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC ) + .intercept( Advice.to( CodeTemplates.ClearDirtyAttributesWithoutCollections.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC ) + .withParameters( boolean.class ) + .intercept( Advice.to( CodeTemplates.SuspendDirtyTracking.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC ) + .intercept( Advice.to( CodeTemplates.GetCollectionTrackerWithoutCollections.class ).wrap( StubMethod.INSTANCE ) ); + } + else { + builder = builder.implement( ExtendedSelfDirtinessTracker.class ) + .defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldManifestation.TRANSIENT, Visibility.PRIVATE ) + .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() ) + .defineField( EnhancerConstants.TRACKER_COLLECTION_NAME, CollectionTracker.class, FieldManifestation.TRANSIENT, Visibility.PRIVATE ) + .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() ) + .defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC ) + .withParameters( String.class ) + .intercept( Advice.to( CodeTemplates.TrackChange.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC ) + .intercept( Advice.to( CodeTemplates.GetDirtyAttributes.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC ) + .intercept( Advice.to( CodeTemplates.AreFieldsDirty.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC ) + .intercept( Advice.to( CodeTemplates.ClearDirtyAttributes.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC ) + .withParameters( boolean.class ) + .intercept( Advice.to( CodeTemplates.SuspendDirtyTracking.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC ) + .intercept( FieldAccessor.ofField( EnhancerConstants.TRACKER_COLLECTION_NAME ) ); - Implementation isDirty = StubMethod.INSTANCE, getDirtyNames = StubMethod.INSTANCE, clearDirtyNames = StubMethod.INSTANCE; - for ( FieldDescription collectionField : collectCollectionFields( managedCtClass ) ) { - if ( !enhancementContext.isMappedCollection( collectionField ) ) { + Implementation isDirty = StubMethod.INSTANCE, getDirtyNames = StubMethod.INSTANCE, clearDirtyNames = StubMethod.INSTANCE; + for ( FieldDescription collectionField : collectCollectionFields( managedCtClass ) ) { if ( collectionField.getType().asErasure().isAssignableTo( Map.class ) ) { isDirty = Advice.withCustomMapping() .bind( CodeTemplates.FieldName.class, collectionField.getName() ) @@ -219,22 +240,22 @@ public class EnhancerImpl implements Enhancer { .wrap( clearDirtyNames ); } } - } - if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) { - clearDirtyNames = Advice.to( CodeTemplates.InitializeLazyAttributeLoadingInterceptor.class ).wrap( clearDirtyNames ); - } + if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) { + clearDirtyNames = Advice.to( CodeTemplates.InitializeLazyAttributeLoadingInterceptor.class ).wrap( clearDirtyNames ); + } - builder = builder.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME, boolean.class, Visibility.PUBLIC ) - .intercept( isDirty ) - .defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, void.class, Visibility.PUBLIC ) - .withParameters( DirtyTracker.class ) - .intercept( getDirtyNames ) - .defineMethod( EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME, void.class, Visibility.PUBLIC ) - .intercept( Advice.withCustomMapping().to( CodeTemplates.ClearDirtyCollectionNames.class ).wrap( StubMethod.INSTANCE ) ) - .defineMethod( ExtendedSelfDirtinessTracker.REMOVE_DIRTY_FIELDS_NAME, void.class, Visibility.PUBLIC ) - .withParameters( LazyAttributeLoadingInterceptor.class ) - .intercept( clearDirtyNames ); + builder = builder.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME, boolean.class, Visibility.PUBLIC ) + .intercept( isDirty ) + .defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, void.class, Visibility.PUBLIC ) + .withParameters( DirtyTracker.class ) + .intercept( getDirtyNames ) + .defineMethod( EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME, void.class, Visibility.PUBLIC ) + .intercept( Advice.withCustomMapping().to( CodeTemplates.ClearDirtyCollectionNames.class ).wrap( StubMethod.INSTANCE ) ) + .defineMethod( ExtendedSelfDirtinessTracker.REMOVE_DIRTY_FIELDS_NAME, void.class, Visibility.PUBLIC ) + .withParameters( LazyAttributeLoadingInterceptor.class ) + .intercept( clearDirtyNames ); + } } return transformer.applyTo( builder, false ); @@ -340,7 +361,7 @@ public class EnhancerImpl implements Enhancer { if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) { continue; } - if ( enhancementContext.isPersistentField( ctField ) ) { + if ( enhancementContext.isPersistentField( ctField ) && !enhancementContext.isMappedCollection( ctField ) ) { if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) { collectionList.add( ctField ); } @@ -368,9 +389,11 @@ public class EnhancerImpl implements Enhancer { List collectionList = new ArrayList(); for ( FieldDescription ctField : managedCtSuperclass.getDeclaredFields() ) { - if ( !Modifier.isStatic( ctField.getModifiers() ) && enhancementContext.isPersistentField( ctField ) ) { - if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) { - collectionList.add( ctField ); + if ( !Modifier.isStatic( ctField.getModifiers() ) ) { + if ( enhancementContext.isPersistentField( ctField ) && !enhancementContext.isMappedCollection( ctField ) ) { + if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) { + collectionList.add( ctField ); + } } } }