HHH-9985 - bytecode enhancer - fix merge use case
This commit is contained in:
parent
463decd245
commit
f759c152e2
|
@ -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",
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.bytecode.enhance.internal.tracker;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by dirty trackers, a simplified Set of String.
|
||||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
public interface DirtyTracker {
|
||||
|
||||
void add(String name);
|
||||
|
||||
boolean contains(String name);
|
||||
|
||||
void clear();
|
||||
|
||||
boolean isEmpty();
|
||||
|
||||
String[] get();
|
||||
}
|
|
@ -16,14 +16,15 @@ import java.util.Arrays;
|
|||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -13,14 +13,15 @@ package org.hibernate.bytecode.enhance.internal.tracker;
|
|||
*
|
||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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<String> lazyFields;
|
||||
private final String entityName;
|
||||
|
||||
private final SimpleDirtyTracker initializedFields = new SimpleDirtyTracker();
|
||||
private final SimpleFieldTracker initializedFields = new SimpleFieldTracker();
|
||||
|
||||
public LazyAttributeLoader(SessionImplementor session, Set<String> 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 + ')';
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ) );
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue