From f759c152e21486b7f677d8e8f84203b64a73b9a4 Mon Sep 17 00:00:00 2001 From: barreiro Date: Sat, 25 Jul 2015 06:26:40 +0100 Subject: [PATCH] HHH-9985 - bytecode enhancer - fix merge use case --- .../enhance/internal/EntityEnhancer.java | 31 +++++++++---------- .../internal/tracker/DirtyTracker.java | 25 +++++++++++++++ ...tyTracker.java => SimpleFieldTracker.java} | 9 ++++-- ...tyTracker.java => SortedFieldTracker.java} | 13 +++++--- .../enhance/spi/EnhancerConstants.java | 23 +++++++++++--- .../spi/interceptor/LazyAttributeLoader.java | 12 +++++-- .../engine/internal/AbstractEntityEntry.java | 4 +-- .../engine/spi/SelfDirtinessTracker.java | 5 +++ .../internal/DefaultMergeEventListener.java | 25 +++++++++++++++ .../bytecode/enhancement/DecompileUtils.java | 1 + .../enhancement/tracker/DirtyTrackerTest.java | 9 +++--- 11 files changed, 121 insertions(+), 36 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/DirtyTracker.java rename hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/{SimpleDirtyTracker.java => SimpleFieldTracker.java} (89%) rename hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/{SortedDirtyTracker.java => SortedFieldTracker.java} (90%) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/EntityEnhancer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/EntityEnhancer.java index d90da2fd04..18ce6ed80d 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/EntityEnhancer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/EntityEnhancer.java @@ -18,7 +18,8 @@ import javassist.Modifier; import javassist.NotFoundException; import org.hibernate.bytecode.enhance.internal.tracker.CollectionTracker; -import org.hibernate.bytecode.enhance.internal.tracker.SimpleDirtyTracker; +import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker; +import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; import org.hibernate.bytecode.enhance.spi.EnhancementContext; import org.hibernate.bytecode.enhance.spi.EnhancementException; import org.hibernate.bytecode.enhance.spi.Enhancer; @@ -36,8 +37,8 @@ public class EntityEnhancer extends Enhancer { super( context ); } - // for very small sizes SimpleDirtyTracker implementation ends up being faster - private static final String TRACKER_IMPL = SimpleDirtyTracker.class.getName(); + // assuming the number of fields is not very high, SimpleFieldTracker implementation it's the fastest + private static final String DIRTY_TRACKER_IMPL = SimpleFieldTracker.class.getName(); public void enhance(CtClass managedCtClass) { // add the ManagedEntity interface @@ -107,7 +108,11 @@ public class EntityEnhancer extends Enhancer { try { managedCtClass.addInterface( classPool.get( SelfDirtinessTracker.class.getName() ) ); - FieldWriter.addField( managedCtClass, classPool.get( TRACKER_IMPL ), EnhancerConstants.TRACKER_FIELD_NAME ); + FieldWriter.addField( + managedCtClass, + classPool.get( DIRTY_TRACKER_IMPL ), + EnhancerConstants.TRACKER_FIELD_NAME + ); FieldWriter.addField( managedCtClass, classPool.get( CollectionTracker.class.getName() ), @@ -124,14 +129,14 @@ public class EntityEnhancer extends Enhancer { private void createDirtyTrackerMethods(CtClass managedCtClass) { try { MethodWriter.write( - managedCtClass, "" + + managedCtClass, "public void %1$s(String name) {%n" + " if (%2$s == null) { %2$s = new %3$s(); }%n" + " %2$s.add(name);%n" + "}", EnhancerConstants.TRACKER_CHANGER_NAME, EnhancerConstants.TRACKER_FIELD_NAME, - TRACKER_IMPL + DIRTY_TRACKER_IMPL ); createCollectionDirtyCheckMethod( managedCtClass ); @@ -139,7 +144,7 @@ public class EntityEnhancer extends Enhancer { createClearDirtyCollectionMethod( managedCtClass ); MethodWriter.write( - managedCtClass, "" + + managedCtClass, "public String[] %1$s() {%n" + " if(%3$s == null) {%n" + " return (%2$s == null) ? new String[0] : %2$s.get();%n" + @@ -153,12 +158,11 @@ public class EntityEnhancer extends Enhancer { EnhancerConstants.TRACKER_FIELD_NAME, EnhancerConstants.TRACKER_COLLECTION_NAME, EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, - TRACKER_IMPL + DIRTY_TRACKER_IMPL ); MethodWriter.write( managedCtClass, - "" + "public boolean %1$s() {%n" + " return (%2$s != null && !%2$s.isEmpty()) || %3$s();%n" + "}", @@ -169,7 +173,6 @@ public class EntityEnhancer extends Enhancer { MethodWriter.write( managedCtClass, - "" + "public void %1$s() {%n" + " if (%2$s != null) { %2$s.clear(); }%n" + " %3$s();%n" + @@ -213,7 +216,6 @@ public class EntityEnhancer extends Enhancer { body.append( String.format( - "" + "private boolean %1$s() {%n" + " if (%2$s == null) {%n" + " return false;%n" + @@ -227,7 +229,6 @@ public class EntityEnhancer extends Enhancer { if ( !enhancementContext.isMappedCollection( ctField ) ) { body.append( String.format( - "" + " // collection field [%1$s]%n" + " if (%1$s == null && %2$s.getSize(\"%1$s\") != -1) { return true; }%n" + " if (%1$s != null && %2$s.getSize(\"%1$s\") != %1$s.size()) { return true; }%n", @@ -252,12 +253,11 @@ public class EntityEnhancer extends Enhancer { body.append( String.format( - "" + "private void %1$s(%3$s tracker) {%n" + " if (%2$s == null) { return; }%n", EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, EnhancerConstants.TRACKER_COLLECTION_NAME, - TRACKER_IMPL + DirtyTracker.class.getName() ) ); @@ -265,7 +265,6 @@ public class EntityEnhancer extends Enhancer { if ( !enhancementContext.isMappedCollection( ctField ) ) { body.append( String.format( - "" + " // Collection field [%1$s]%n" + " if (%1$s == null && %2$s.getSize(\"%1$s\") != -1) { tracker.add(\"%1$s\"); }%n" + " if (%1$s != null && %2$s.getSize(\"%1$s\") != %1$s.size()) { tracker.add(\"%1$s\"); }%n", @@ -290,7 +289,6 @@ public class EntityEnhancer extends Enhancer { body.append( String.format( - "" + "private void %1$s() {%n" + " if (%2$s == null) { %2$s = new %3$s(); }%n", EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME, @@ -303,7 +301,6 @@ public class EntityEnhancer extends Enhancer { if ( !enhancementContext.isMappedCollection( ctField ) ) { body.append( String.format( - "" + " // Collection field [%1$s]%n" + " if (%1$s == null) { %2$s.add(\"%1$s\", -1); }%n" + " else { %2$s.add(\"%1$s\", %1$s.size()); }%n", diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/DirtyTracker.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/DirtyTracker.java new file mode 100644 index 0000000000..710696fb66 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/DirtyTracker.java @@ -0,0 +1,25 @@ +/* + * 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 . + */ +package org.hibernate.bytecode.enhance.internal.tracker; + +/** + * Interface to be implemented by dirty trackers, a simplified Set of String. + * + * @author Luis Barreiro + */ +public interface DirtyTracker { + + void add(String name); + + boolean contains(String name); + + void clear(); + + boolean isEmpty(); + + String[] get(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SimpleDirtyTracker.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SimpleFieldTracker.java similarity index 89% rename from hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SimpleDirtyTracker.java rename to hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SimpleFieldTracker.java index efd22f8c7a..b1a7ef1092 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SimpleDirtyTracker.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SimpleFieldTracker.java @@ -16,14 +16,15 @@ import java.util.Arrays; * * @author Luis Barreiro */ -public final class SimpleDirtyTracker { +public final class SimpleFieldTracker implements DirtyTracker { private String[] names; - public SimpleDirtyTracker() { + public SimpleFieldTracker() { names = new String[0]; } + @Override public void add(String name) { if ( !contains( name ) ) { names = Arrays.copyOf( names, names.length + 1 ); @@ -31,6 +32,7 @@ public final class SimpleDirtyTracker { } } + @Override public boolean contains(String name) { for ( String existing : names ) { if ( existing.equals( name ) ) { @@ -40,14 +42,17 @@ public final class SimpleDirtyTracker { return false; } + @Override public void clear() { names = new String[0]; } + @Override public boolean isEmpty() { return names.length == 0; } + @Override public String[] get() { return names; } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SortedDirtyTracker.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SortedFieldTracker.java similarity index 90% rename from hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SortedDirtyTracker.java rename to hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SortedFieldTracker.java index 73fef9fdf5..d6526ba6d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SortedDirtyTracker.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SortedFieldTracker.java @@ -13,14 +13,15 @@ package org.hibernate.bytecode.enhance.internal.tracker; * * @author Luis Barreiro */ -public final class SortedDirtyTracker { +public final class SortedFieldTracker implements DirtyTracker { private String[] names; - public SortedDirtyTracker() { + public SortedFieldTracker() { names = new String[0]; } + @Override public void add(String name) { // we do a binary search: even if we don't find the name at least we get the position to insert into the array int insert = 0; @@ -41,12 +42,13 @@ public final class SortedDirtyTracker { } } final String[] newNames = new String[names.length + 1]; - System.arraycopy( names, 0, newNames, 0, insert); - System.arraycopy( names, insert, newNames, insert + 1, names.length - insert); + System.arraycopy( names, 0, newNames, 0, insert ); + System.arraycopy( names, insert, newNames, insert + 1, names.length - insert ); newNames[insert] = name; names = newNames; } + @Override public boolean contains(String name) { for ( int low = 0, high = names.length - 1; low <= high; ) { final int middle = low + ( ( high - low ) / 2 ); @@ -66,14 +68,17 @@ public final class SortedDirtyTracker { return false; } + @Override public void clear() { names = new String[0]; } + @Override public boolean isEmpty() { return names.length == 0; } + @Override public String[] get() { return names; } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancerConstants.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancerConstants.java index aeea1434e7..f9f16741e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancerConstants.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancerConstants.java @@ -11,7 +11,8 @@ package org.hibernate.bytecode.enhance.spi; * * @author Steve Ebersole */ -public class EnhancerConstants { +public final class EnhancerConstants { + /** * Prefix for persistent-field reader methods. */ @@ -141,22 +142,34 @@ public class EnhancerConstants { */ public static final String TRACKER_COLLECTION_CHANGED_NAME = "$$_hibernate_areCollectionFieldsDirty"; + /** + * Name of the field that holds the collection tracker + */ public static final String TRACKER_COLLECTION_NAME = "$$_hibernate_collectionTracker"; + /** * Name of method to get dirty collection field names */ public static final String TRACKER_COLLECTION_CHANGED_FIELD_NAME = "$$_hibernate_getCollectionFieldDirtyNames"; + /** + * Name of method to clear dirty attribute on collection fields + */ public static final String TRACKER_COLLECTION_CLEAR_NAME = "$$_hibernate_clearDirtyCollectionNames"; - public static final String TRACKER_COMPOSITE_DIRTY_CHECK = "$$_hibernate_areCompositeFieldsDirty"; - - public static final String TRACKER_COMPOSITE_DIRTY_FIELDS_GETTER = "$$_hibernate_getCompositeDirtyFields"; - + /** + * Field to hold the track the owner of the embeddable entity + */ public static final String TRACKER_COMPOSITE_FIELD_NAME = "$$_hibernate_compositeOwners"; + /** + * Method to set the owner of the embedded entity + */ public static final String TRACKER_COMPOSITE_SET_OWNER = "$$_hibernate_setOwner"; + /** + * Method to clear the owner of the embedded entity + */ public static final String TRACKER_COMPOSITE_CLEAR_OWNER = "$$_hibernate_clearOwner"; private EnhancerConstants() { diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoader.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoader.java index b2c1cd760e..fc6e816add 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoader.java @@ -10,7 +10,7 @@ package org.hibernate.bytecode.enhance.spi.interceptor; import java.util.Set; import org.hibernate.LazyInitializationException; -import org.hibernate.bytecode.enhance.internal.tracker.SimpleDirtyTracker; +import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SessionImplementor; @@ -26,7 +26,7 @@ public class LazyAttributeLoader implements PersistentAttributeInterceptor { private final Set lazyFields; private final String entityName; - private final SimpleDirtyTracker initializedFields = new SimpleDirtyTracker(); + private final SimpleFieldTracker initializedFields = new SimpleFieldTracker(); public LazyAttributeLoader(SessionImplementor session, Set lazyFields, String entityName) { this.session = session; @@ -58,6 +58,14 @@ public class LazyAttributeLoader implements PersistentAttributeInterceptor { } } + public void setLoaded(String attributeName) { + initializedFields.add( attributeName ); + } + + public String[] getiInitializedFields() { + return initializedFields.get(); + } + @Override public String toString() { return "LazyAttributeLoader(entityName=" + entityName + " ,lazyFields=" + lazyFields + ')'; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java index 5bea0cfe13..becae03151 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java @@ -287,7 +287,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry { } if( entity instanceof SelfDirtinessTracker ) { - ((SelfDirtinessTracker) entity).$$_hibernate_clearDirtyAttributes(); + ( (SelfDirtinessTracker) entity ).$$_hibernate_clearDirtyAttributes(); } persistenceContext.getSession() @@ -342,7 +342,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry { private boolean isUnequivocallyNonDirty(Object entity) { if(entity instanceof SelfDirtinessTracker) { - return ((SelfDirtinessTracker) entity).$$_hibernate_hasDirtyAttributes(); + return ! ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes(); } final CustomEntityDirtinessStrategy customEntityDirtinessStrategy = diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java index 9f3e2fb2d5..3a94c2ea63 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SelfDirtinessTracker.java @@ -32,6 +32,11 @@ public interface SelfDirtinessTracker { */ String[] $$_hibernate_getDirtyAttributes(); + /** + * Adds persistent attribute to the set of values that have changed + */ + void $$_hibernate_trackChange(String attributes); + /** * Clear the stored dirty attributes */ diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java index 6decd452d7..0f09e6f8c5 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java @@ -15,6 +15,7 @@ import org.hibernate.ObjectDeletedException; import org.hibernate.StaleObjectStateException; import org.hibernate.WrongClassException; import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoader; import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.internal.Cascade; @@ -23,6 +24,9 @@ import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; +import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EntityCopyObserver; @@ -341,6 +345,27 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme interceptor.dirty(); } } + + // for enhanced entities, copy over the dirty attributes and the lazy/loaded fields in the interceptor + if ( entity instanceof SelfDirtinessTracker && target instanceof SelfDirtinessTracker ) { + for ( String fieldName : ( (SelfDirtinessTracker) entity ).$$_hibernate_getDirtyAttributes() ) { + ( (SelfDirtinessTracker) target ).$$_hibernate_trackChange( fieldName ); + } + } + if ( entity instanceof PersistentAttributeInterceptable + && target instanceof PersistentAttributeInterceptable + && ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor() != null + && ( (PersistentAttributeInterceptable) target ).$$_hibernate_getInterceptor() != null ) { + + PersistentAttributeInterceptor entityInterceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor(); + PersistentAttributeInterceptor targetInterceptor = ( (PersistentAttributeInterceptable) target ).$$_hibernate_getInterceptor(); + + if ( entityInterceptor instanceof LazyAttributeLoader && targetInterceptor instanceof LazyAttributeLoader ) { + for ( String fieldName : ( (LazyAttributeLoader) entityInterceptor ).getiInitializedFields() ) { + ( (LazyAttributeLoader) targetInterceptor ).setLoaded( fieldName ); + } + } + } } private boolean isVersionChanged(Object entity, EventSource source, EntityPersister persister, Object target) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/DecompileUtils.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/DecompileUtils.java index 7bbd4be1f6..31a98502b6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/DecompileUtils.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/DecompileUtils.java @@ -98,6 +98,7 @@ public abstract class DecompileUtils { } if ( interfaceNames.contains( SelfDirtinessTracker.class.getName() ) ) { assertTrue( fieldNames.contains( EnhancerConstants.TRACKER_FIELD_NAME ) ); + assertTrue( methodNames.contains( EnhancerConstants.TRACKER_CHANGER_NAME ) ); assertTrue( methodNames.contains( EnhancerConstants.TRACKER_GET_NAME ) ); assertTrue( methodNames.contains( EnhancerConstants.TRACKER_CLEAR_NAME ) ); assertTrue( methodNames.contains( EnhancerConstants.TRACKER_HAS_CHANGED_NAME ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/tracker/DirtyTrackerTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/tracker/DirtyTrackerTest.java index b64b37e15f..cbd64baece 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/tracker/DirtyTrackerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/tracker/DirtyTrackerTest.java @@ -6,8 +6,9 @@ */ package org.hibernate.test.bytecode.enhancement.tracker; -import org.hibernate.bytecode.enhance.internal.tracker.SimpleDirtyTracker; -import org.hibernate.bytecode.enhance.internal.tracker.SortedDirtyTracker; +import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker; +import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; +import org.hibernate.bytecode.enhance.internal.tracker.SortedFieldTracker; import org.junit.Test; @@ -22,7 +23,7 @@ public class DirtyTrackerTest { @Test public void testSimpleTracker() { - SimpleDirtyTracker tracker = new SimpleDirtyTracker(); + DirtyTracker tracker = new SimpleFieldTracker(); assertTrue(tracker.isEmpty()); assertTrue(tracker.get().length == 0); @@ -46,7 +47,7 @@ public class DirtyTrackerTest { @Test public void testSortedTracker() { - SortedDirtyTracker tracker = new SortedDirtyTracker(); + DirtyTracker tracker = new SortedFieldTracker(); assertTrue(tracker.isEmpty()); assertTrue(tracker.get().length == 0);