diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java index 4429c81c58..06025c2462 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java @@ -27,12 +27,18 @@ import org.hibernate.internal.util.compare.EqualsHelper; */ public abstract class AttributeTypeDescriptor { + protected InheritanceMetadata inheritanceMetadata; + + protected AttributeTypeDescriptor(InheritanceMetadata inheritanceMetadata) { + this.inheritanceMetadata = inheritanceMetadata; + } + public abstract String buildReadInterceptionBodyFragment(String fieldName); public abstract String buildWriteInterceptionBodyFragment(String fieldName); public String buildInLineDirtyCheckingBodyFragment(EnhancementContext context, CtField currentValue) { - final StringBuilder builder = new StringBuilder(); + StringBuilder builder = new StringBuilder(); try { // should ignore primary keys if ( PersistentAttributesHelper.hasAnnotation( currentValue, Id.class ) @@ -40,9 +46,13 @@ public abstract class AttributeTypeDescriptor { return ""; } + String readFragment = inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() + ? "super." + inheritanceMetadata.getReaderName() + "()" + : "this." + currentValue.getName(); + if ( currentValue.getType().isPrimitive() || currentValue.getType().isEnum() ) { // primitives || enums - builder.append( String.format( " if (%s != $1)", currentValue.getName() ) ); + builder.append( String.format( " if ( %s != $1 )", readFragment ) ); } else { // if the field is a collection we return since we handle that in a separate method @@ -58,14 +68,13 @@ public abstract class AttributeTypeDescriptor { String.format( " if ( !%s.areEqual( %s, $1 ) )", EqualsHelper.class.getName(), - currentValue.getName() + readFragment ) ); } builder.append( String.format( " { %s(\"%s\"); }", EnhancerConstants.TRACKER_CHANGER_NAME, currentValue.getName() ) ); } - catch (NotFoundException e) { - e.printStackTrace(); + catch (NotFoundException ignore) { } return builder.toString(); } @@ -75,33 +84,39 @@ public abstract class AttributeTypeDescriptor { /** * factory method to get the AttributeTypeDescriptor for a particular field type */ - public static AttributeTypeDescriptor resolve(CtField persistentField) throws NotFoundException { - if ( persistentField.getType() == CtClass.booleanType ) { - return new PrimitiveAttributeTypeDescriptor( Boolean.TYPE ); + public static AttributeTypeDescriptor resolve(CtClass managedCtClass, CtField persistentField) throws NotFoundException { + boolean inherited = !managedCtClass.equals( persistentField.getDeclaringClass() ); + boolean visible = persistentField.visibleFrom( managedCtClass ); + String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName(); + String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + persistentField.getName(); + InheritanceMetadata inheritanceMetadata = new InheritanceMetadata( inherited, visible, readerName, writerName ); + + if ( CtClass.booleanType.equals( persistentField.getType() ) ) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Boolean.TYPE ); } - else if ( persistentField.getType() == CtClass.byteType ) { - return new PrimitiveAttributeTypeDescriptor( Byte.TYPE ); + else if ( CtClass.byteType.equals( persistentField.getType() )) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Byte.TYPE ); } - else if ( persistentField.getType() == CtClass.charType ) { - return new PrimitiveAttributeTypeDescriptor( Character.TYPE ); + else if ( CtClass.charType.equals( persistentField.getType() ) ) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Character.TYPE ); } - else if ( persistentField.getType() == CtClass.shortType ) { - return new PrimitiveAttributeTypeDescriptor( Short.TYPE ); + else if ( CtClass.shortType.equals( persistentField.getType() ) ) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Short.TYPE ); } - else if ( persistentField.getType() == CtClass.intType ) { - return new PrimitiveAttributeTypeDescriptor( Integer.TYPE ); + else if ( CtClass.intType.equals( persistentField.getType() ) ) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Integer.TYPE ); } - else if ( persistentField.getType() == CtClass.longType ) { - return new PrimitiveAttributeTypeDescriptor( Long.TYPE ); + else if ( CtClass.longType.equals( persistentField.getType() ) ) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Long.TYPE ); } - else if ( persistentField.getType() == CtClass.doubleType ) { - return new PrimitiveAttributeTypeDescriptor( Double.TYPE ); + else if ( CtClass.doubleType.equals( persistentField.getType() ) ) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Double.TYPE ); } - else if ( persistentField.getType() == CtClass.floatType ) { - return new PrimitiveAttributeTypeDescriptor( Float.TYPE ); + else if ( CtClass.floatType.equals( persistentField.getType() ) ) { + return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Float.TYPE ); } else { - return new ObjectAttributeTypeDescriptor( persistentField.getType() ); + return new ObjectAttributeTypeDescriptor( inheritanceMetadata, persistentField.getType() ); } } @@ -114,26 +129,53 @@ public abstract class AttributeTypeDescriptor { private final String type; - private ObjectAttributeTypeDescriptor(CtClass concreteType) { + private ObjectAttributeTypeDescriptor(InheritanceMetadata inheritanceMetadata, CtClass concreteType) { + super( inheritanceMetadata ); this.type = concreteType.getName(); } + @Override public String buildReadInterceptionBodyFragment(String fieldName) { - return String.format( - " if ( %3$s() != null ) { this.%1$s = (%2$s) %3$s().readObject(this, \"%1$s\", this.%1$s); }%n", - fieldName, - type, - EnhancerConstants.INTERCEPTOR_GETTER_NAME ); + if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) { + return String.format( + " if( %3$s() != null ) { super.%5$s( (%2$s) %3$s().readObject(this, \"%1$s\", super.%4$s())); }%n", + fieldName, + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME, + inheritanceMetadata.getReaderName(), + inheritanceMetadata.getWriterName() ); + } + else { + return String.format( + " if ( %3$s() != null ) { this.%1$s = (%2$s) %3$s().readObject(this, \"%1$s\", this.%1$s); }%n", + fieldName, + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME ); + } } + @Override public String buildWriteInterceptionBodyFragment(String fieldName) { - return String.format( - " %2$s localVar = $1;%n" + - " if ( %3$s() != null ) { localVar = (%2$s) %3$s().writeObject(this, \"%1$s\", this.%1$s, $1); }%n" + - " this.%1$s = localVar;", - fieldName, - type, - EnhancerConstants.INTERCEPTOR_GETTER_NAME ); + if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) { + return String.format( + " %2$s localVar = $1;%n" + + " if ( %3$s() != null ) { localVar = (%2$s) %3$s().writeObject(this, \"%1$s\", super.%4$s(), $1); }%n" + + " super.%5$s(localVar);", + fieldName, + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME, + inheritanceMetadata.getReaderName(), + inheritanceMetadata.getWriterName() ); + } + else { + return String.format( + " %2$s localVar = $1;%n" + + " if ( %3$s() != null ) { localVar = (%2$s) %3$s().writeObject(this, \"%1$s\", this.%1$s, $1); }%n" + + " this.%1$s = localVar;", + fieldName, + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME ); + } } } @@ -144,7 +186,8 @@ public abstract class AttributeTypeDescriptor { private final String type; - private PrimitiveAttributeTypeDescriptor(Class primitiveType) { + private PrimitiveAttributeTypeDescriptor(InheritanceMetadata inheritanceMetadata, Class primitiveType) { + super( inheritanceMetadata ); if ( !primitiveType.isPrimitive() ) { throw new IllegalArgumentException( "Primitive attribute type descriptor can only be used on primitive types" ); } @@ -152,24 +195,84 @@ public abstract class AttributeTypeDescriptor { this.type = primitiveType.getSimpleName().substring( 0, 1 ).toUpperCase( Locale.ROOT ) + primitiveType.getSimpleName().substring( 1 ); } + @Override public String buildReadInterceptionBodyFragment(String fieldName) { - return String.format( - " if (%3$s() != null ) { this.%1$s = %3$s().read%2$s(this, \"%1$s\", this.%1$s); }", - fieldName, - type, - EnhancerConstants.INTERCEPTOR_GETTER_NAME ); + if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) { + return String.format( + " if (%3$s() != null ) { super.%5$s( %3$s().read%2$s(this, \"%1$s\", super.%4$s())); }", + fieldName, + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME, + inheritanceMetadata.getReaderName(), + inheritanceMetadata.getWriterName() ); + } + else { + return String.format( + " if (%3$s() != null ) { this.%1$s = %3$s().read%2$s(this, \"%1$s\", this.%1$s); }", + fieldName, + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME ); + } } + @Override public String buildWriteInterceptionBodyFragment(String fieldName) { - return String.format( - " %2$s localVar = $1;%n" + - " if ( %4$s() != null ) { localVar = %4$s().write%3$s(this, \"%1$s\", this.%1$s, $1); }%n" + - " this.%1$s = localVar;", - fieldName, - type.toLowerCase( Locale.ROOT ), - type, - EnhancerConstants.INTERCEPTOR_GETTER_NAME - ); + if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) { + return String.format( + " %2$s localVar = $1;%n" + + " if ( %4$s() != null ) { localVar = %4$s().write%3$s(this, \"%1$s\", super.%5$s(), $1); }%n" + + " super.%6$s(localVar);", + fieldName, + type.toLowerCase( Locale.ROOT ), + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME, + inheritanceMetadata.getReaderName(), + inheritanceMetadata.getWriterName() ); + } + else { + return String.format( + " %2$s localVar = $1;%n" + + " if ( %4$s() != null ) { localVar = %4$s().write%3$s(this, \"%1$s\", this.%1$s, $1); }%n" + + " this.%1$s = localVar;", + fieldName, + type.toLowerCase( Locale.ROOT ), + type, + EnhancerConstants.INTERCEPTOR_GETTER_NAME + ); + } + } + } + + // + + private static class InheritanceMetadata { + + private boolean inherited; + private boolean visible; + private String readerName; + private String writerName; + + public InheritanceMetadata(boolean inherited, boolean visible, String readerName, String writerName) { + this.inherited = inherited; + this.visible = visible; + this.readerName = readerName; + this.writerName = writerName; + } + + public boolean isInherited() { + return inherited; + } + + public boolean isVisible() { + return visible; + } + + public String getReaderName() { + return readerName; + } + + public String getWriterName() { + return writerName; } } 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 e4a2d124a7..8d015f2a80 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 @@ -6,7 +6,9 @@ */ package org.hibernate.bytecode.enhance.internal; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -17,6 +19,7 @@ import javassist.CtClass; import javassist.CtField; import javassist.Modifier; +import javassist.NotFoundException; import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker; import org.hibernate.bytecode.enhance.internal.tracker.SimpleCollectionTracker; import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker; @@ -206,7 +209,7 @@ public class EntityEnhancer extends PersistentAttributesEnhancer { } private List collectCollectionFields(CtClass managedCtClass) { - final List collectionList = new LinkedList(); + List collectionList = new ArrayList<>(); for ( CtField ctField : managedCtClass.getDeclaredFields() ) { // skip static fields and skip fields added by enhancement @@ -222,21 +225,42 @@ public class EntityEnhancer extends PersistentAttributesEnhancer { } // HHH-10646 Add fields inherited from @MappedSuperclass - for ( CtField ctField : managedCtClass.getDeclaredFields() ) { - if ( !enhancementContext.isMappedSuperclassClass( ctField.getDeclaringClass() ) || Modifier.isStatic( ctField.getModifiers() ) ) { - continue; - } - if ( enhancementContext.isPersistentField( ctField ) ) { - if ( PersistentAttributesHelper.isAssignable( ctField, Collection.class.getName() ) || - PersistentAttributesHelper.isAssignable( ctField, Map.class.getName() ) ) { - collectionList.add( ctField ); - } - } + // HHH-10981 There is no need to do it for @MappedSuperclass + if ( !enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { + collectionList.addAll( collectInheritCollectionFields( managedCtClass ) ); } return collectionList; } + private Collection collectInheritCollectionFields(CtClass managedCtClass) { + if ( managedCtClass == null || Object.class.getName().equals( managedCtClass.getName() ) ) { + return Collections.emptyList(); + } + try { + CtClass managedCtSuperclass = managedCtClass.getSuperclass(); + + if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass ) ) { + return collectInheritCollectionFields( managedCtSuperclass ); + } + List collectionList = new ArrayList(); + + for ( CtField ctField : managedCtSuperclass.getDeclaredFields() ) { + if ( !Modifier.isStatic( ctField.getModifiers() ) && enhancementContext.isPersistentField( ctField ) ) { + if ( PersistentAttributesHelper.isAssignable( ctField, Collection.class.getName() ) || + PersistentAttributesHelper.isAssignable( ctField, Map.class.getName() ) ) { + collectionList.add( ctField ); + } + } + } + collectionList.addAll( collectInheritCollectionFields( managedCtSuperclass ) ); + return collectionList; + } + catch ( NotFoundException nfe ) { + return Collections.emptyList(); + } + } + private void createCollectionDirtyCheckMethod(CtClass managedCtClass) { try { final StringBuilder body = new StringBuilder(); diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/PersistentAttributesEnhancer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/PersistentAttributesEnhancer.java index 620d1245b0..18783d81a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/PersistentAttributesEnhancer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/PersistentAttributesEnhancer.java @@ -6,6 +6,10 @@ */ package org.hibernate.bytecode.enhance.internal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; @@ -76,42 +80,60 @@ public class PersistentAttributesEnhancer extends Enhancer { } private CtField[] collectPersistentFields(CtClass managedCtClass) { - final List persistentFieldList = new LinkedList(); + List persistentFieldList = new ArrayList(); for ( CtField ctField : managedCtClass.getDeclaredFields() ) { - // skip static fields and skip fields added by enhancement - if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) { + // skip static fields and skip fields added by enhancement and outer reference in inner classes + if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) { continue; } - // skip outer reference in inner classes - if ( "this$0".equals( ctField.getName() ) ) { - continue; - } - if ( enhancementContext.isPersistentField( ctField ) ) { + if ( !Modifier.isStatic( ctField.getModifiers() ) && enhancementContext.isPersistentField( ctField ) ) { persistentFieldList.add( ctField ); } } // HHH-10646 Add fields inherited from @MappedSuperclass - // CtClass.getFields() does not return private fields, while CtClass.getDeclaredFields() does not return inherit - for ( CtField ctField : managedCtClass.getFields() ) { - if ( ctField.getDeclaringClass().equals( managedCtClass ) ) { - // Already processed above - continue; - } - if ( !enhancementContext.isMappedSuperclassClass( ctField.getDeclaringClass() ) || Modifier.isStatic( ctField.getModifiers() ) ) { - continue; - } - if ( enhancementContext.isPersistentField( ctField ) ) { - persistentFieldList.add( ctField ); - } + // HHH-10981 There is no need to do it for @MappedSuperclass + if ( !enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { + persistentFieldList.addAll( collectInheritPersistentFields( managedCtClass ) ); } - return enhancementContext.order( persistentFieldList.toArray( new CtField[persistentFieldList.size()] ) ); + + CtField[] orderedFields = enhancementContext.order( persistentFieldList.toArray( new CtField[0] ) ); + log.debugf( "Persistent fields for entity %s: %s", managedCtClass.getName(), Arrays.toString( orderedFields )); + return orderedFields; } - private PersistentAttributeAccessMethods enhancePersistentAttribute( - CtClass managedCtClass, - CtField persistentField) { + private Collection collectInheritPersistentFields(CtClass managedCtClass) { + if ( managedCtClass == null || Object.class.getName().equals( managedCtClass.getName() ) ) { + return Collections.emptyList(); + } try { - final AttributeTypeDescriptor typeDescriptor = AttributeTypeDescriptor.resolve( persistentField ); + CtClass managedCtSuperclass = managedCtClass.getSuperclass(); + + if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass ) ) { + return collectInheritPersistentFields( managedCtSuperclass ); + } + log.debugf( "Found @MappedSuperclass %s to collectPersistenceFields", managedCtSuperclass.getName() ); + List persistentFieldList = new ArrayList(); + + for ( CtField ctField : managedCtSuperclass.getDeclaredFields() ) { + if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) { + continue; + } + if ( !Modifier.isStatic( ctField.getModifiers() ) && enhancementContext.isPersistentField( ctField ) ) { + persistentFieldList.add( ctField ); + } + } + persistentFieldList.addAll( collectInheritPersistentFields( managedCtSuperclass ) ); + return persistentFieldList; + } + catch ( NotFoundException nfe ) { + log.warnf( "Could not find the superclass of %s", managedCtClass ); + return Collections.emptyList(); + } + } + + private PersistentAttributeAccessMethods enhancePersistentAttribute( CtClass managedCtClass, CtField persistentField) { + try { + AttributeTypeDescriptor typeDescriptor = AttributeTypeDescriptor.resolve( managedCtClass, persistentField ); return new PersistentAttributeAccessMethods( generateFieldReader( managedCtClass, persistentField, typeDescriptor ), generateFieldWriter( managedCtClass, persistentField, typeDescriptor ) @@ -131,24 +153,57 @@ public class PersistentAttributesEnhancer extends Enhancer { CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) { - final String fieldName = persistentField.getName(); - final String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName; - - // read attempts only have to deal lazy-loading support, not dirty checking; - // so if the field is not enabled as lazy-loadable return a plain simple getter as the reader - if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) - || !enhancementContext.isLazyLoadable( persistentField ) ) { - return MethodWriter.addGetter( managedCtClass, fieldName, readerName ); - } + String fieldName = persistentField.getName(); + String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName; + String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName; + CtMethod tmpSuperReader = null; + CtMethod tmpSuperWriter = null; + CtMethod reader = null; try { - return MethodWriter.write( - managedCtClass, "public %s %s() {%n%s%n return this.%s;%n}", - persistentField.getType().getName(), - readerName, - typeDescriptor.buildReadInterceptionBodyFragment( fieldName ), - fieldName - ); + boolean declared = persistentField.getDeclaringClass().equals( managedCtClass ); + String declaredReadFragment = "this." + fieldName + ""; + String superReadFragment = "super." + readerName + "()"; + + if (!declared) { + // create a temporary getter on the supper entity to be able to compile our code + try { + persistentField.getDeclaringClass().getDeclaredMethod( readerName ); + persistentField.getDeclaringClass().getDeclaredMethod( writerName ); + } + catch (NotFoundException nfe){ + tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName ); + tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName ); + } + } + + // read attempts only have to deal lazy-loading support, not dirty checking; + // so if the field is not enabled as lazy-loadable return a plain simple getter as the reader + if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) + || !enhancementContext.isLazyLoadable( persistentField ) ) { + reader = MethodWriter.write( + managedCtClass, "public %s %s() { return %s;%n}", + persistentField.getType().getName(), + readerName, + declared ? declaredReadFragment : superReadFragment + ); + } + else { + reader = MethodWriter.write( + managedCtClass, "public %s %s() {%n%s%n return %s;%n}", + persistentField.getType().getName(), + readerName, + typeDescriptor.buildReadInterceptionBodyFragment( fieldName ), + declared ? declaredReadFragment : superReadFragment + ); + } + if ( tmpSuperReader != null ) { + persistentField.getDeclaringClass().removeMethod( tmpSuperReader ); + } + if ( tmpSuperWriter != null ) { + persistentField.getDeclaringClass().removeMethod( tmpSuperWriter ); + } + return reader; } catch (CannotCompileException cce) { final String msg = String.format( @@ -172,15 +227,39 @@ public class PersistentAttributesEnhancer extends Enhancer { CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) { - final String fieldName = persistentField.getName(); - final String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName; + String fieldName = persistentField.getName(); + String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName; + String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName; + CtMethod tmpSuperReader = null; + CtMethod tmpSuperWriter = null; + CtMethod writer; try { - final CtMethod writer; + boolean declared = persistentField.getDeclaringClass().equals( managedCtClass ); + String declaredWriteFragment = "this." + fieldName + "=" + fieldName + ";"; + String superWriteFragment = "super." + writerName + "(" + fieldName + ");"; - if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) - || !enhancementContext.isLazyLoadable( persistentField ) ) { - writer = MethodWriter.addSetter( managedCtClass, fieldName, writerName ); + if (!declared) { + // create a temporary setter on the supper entity to be able to compile our code + try { + persistentField.getDeclaringClass().getDeclaredMethod( readerName ); + persistentField.getDeclaringClass().getDeclaredMethod( writerName ); + } + catch (NotFoundException nfe){ + tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName ); + tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName ); + } + } + + if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( persistentField ) ) { + writer = MethodWriter.write( + managedCtClass, + "public void %s(%s %s) {%n %s%n}", + writerName, + persistentField.getType().getName(), + fieldName, + declared ? declaredWriteFragment : superWriteFragment + ); } else { writer = MethodWriter.write( @@ -203,12 +282,7 @@ public class PersistentAttributesEnhancer extends Enhancer { ); } else { - writer.insertBefore( - typeDescriptor.buildInLineDirtyCheckingBodyFragment( - enhancementContext, - persistentField - ) - ); + writer.insertBefore( typeDescriptor.buildInLineDirtyCheckingBodyFragment( enhancementContext, persistentField ) ); } handleCompositeField( managedCtClass, persistentField, writer ); @@ -217,6 +291,13 @@ public class PersistentAttributesEnhancer extends Enhancer { if ( enhancementContext.doBiDirectionalAssociationManagement( persistentField ) ) { handleBiDirectionalAssociation( managedCtClass, persistentField, writer ); } + + if ( tmpSuperReader != null ) { + persistentField.getDeclaringClass().removeMethod( tmpSuperReader ); + } + if ( tmpSuperWriter != null ) { + persistentField.getDeclaringClass().removeMethod( tmpSuperWriter ); + } return writer; } catch (CannotCompileException cce) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTest.java index 167714952a..238651d502 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTest.java @@ -172,6 +172,13 @@ public class EnhancerTest extends BaseUnitTestCase { @TestForIssue( jiraKey = "HHH-10646" ) public void testMappedSuperclass() { EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class ); + EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class, new EnhancerTestContext() { + @Override + public boolean hasLazyLoadableAttributes(CtClass classDescriptor) { + // HHH-10981 - Without lazy loading, the generation of getters and setters has a different code path + return false; + } + } ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mapped/MappedSuperclassTestTask.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mapped/MappedSuperclassTestTask.java index a7ca91e861..5b22712939 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mapped/MappedSuperclassTestTask.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/mapped/MappedSuperclassTestTask.java @@ -29,7 +29,7 @@ public class MappedSuperclassTestTask extends AbstractEnhancerTestTask { public void execute() { Employee charles = new Employee( "Charles", "Engineer" ); - charles.oca = 1002; + charles.setOca( 1002 ); // Check that both types of class attributes are being dirty tracked EnhancerTestUtils.checkDirtyTracking( charles, "title", "oca" ); @@ -47,9 +47,9 @@ public class MappedSuperclassTestTask extends AbstractEnhancerTestTask { @MappedSuperclass private static class Person { - @Id String name; + @Id private String name; - @Version long oca; + @Version private long oca; public Person(String name) { this();