diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/BiDirectionalAssociationHandler.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/BiDirectionalAssociationHandler.java index d5e35473c6..9e120435a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/BiDirectionalAssociationHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/BiDirectionalAssociationHandler.java @@ -9,6 +9,7 @@ package org.hibernate.bytecode.enhance.internal.bytebuddy; import java.util.Collection; import java.util.Map; import java.util.Objects; +import java.util.Optional; import javax.persistence.Access; import javax.persistence.AccessType; @@ -166,12 +167,12 @@ final class BiDirectionalAssociationHandler implements Implementation { return persistentField.getType(); } else { - MethodDescription getter = EnhancerImpl.getterOf( persistentField ); - if ( getter == null ) { - return persistentField.getType(); + Optional getter = persistentField.getGetter(); + if ( getter.isPresent() ) { + return getter.get().getReturnType(); } else { - return getter.getReturnType(); + return persistentField.getType(); } } } @@ -211,7 +212,7 @@ final class BiDirectionalAssociationHandler implements Implementation { private static String getMappedByManyToMany(AnnotatedFieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) { for ( FieldDescription f : targetEntity.getDeclaredFields() ) { - AnnotatedFieldDescription annotatedF = new AnnotatedFieldDescription( f ); + AnnotatedFieldDescription annotatedF = new AnnotatedFieldDescription( context, f ); if ( context.isPersistentField( annotatedF ) && target.getName().equals( getMappedByNotManyToMany( annotatedF ) ) && target.getDeclaringType().asErasure().isAssignableTo( entityType( annotatedF.getType() ) ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java index 40df641c4f..1cb20ff0e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java @@ -6,15 +6,33 @@ */ package org.hibernate.bytecode.enhance.internal.bytebuddy; +import static net.bytebuddy.matcher.ElementMatchers.isGetter; + +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.hibernate.MappingException; import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl.AnnotatedFieldDescription; import org.hibernate.bytecode.enhance.spi.EnhancementContext; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.scaffold.MethodGraph; +import net.bytebuddy.matcher.ElementMatcher; class ByteBuddyEnhancementContext { + private static final ElementMatcher.Junction IS_GETTER = isGetter(); + private final EnhancementContext enhancementContext; + private final ConcurrentHashMap> getterByTypeMap = new ConcurrentHashMap<>(); + ByteBuddyEnhancementContext(EnhancementContext enhancementContext) { this.enhancementContext = enhancementContext; } @@ -66,4 +84,40 @@ class ByteBuddyEnhancementContext { public boolean doBiDirectionalAssociationManagement(AnnotatedFieldDescription field) { return enhancementContext.doBiDirectionalAssociationManagement( field ); } + + Optional resolveGetter(FieldDescription fieldDescription) { + Map getters = getterByTypeMap + .computeIfAbsent( fieldDescription.getDeclaringType().asErasure(), declaringType -> { + return MethodGraph.Compiler.DEFAULT.compile( declaringType ) + .listNodes() + .asMethodList() + .filter( IS_GETTER ) + .stream() + .collect( Collectors.toMap( MethodDescription::getActualName, Function.identity() ) ); + } ); + + String capitalizedFieldName = Character.toUpperCase( fieldDescription.getName().charAt( 0 ) ) + + fieldDescription.getName().substring( 1 ); + + MethodDescription getCandidate = getters.get( "get" + capitalizedFieldName ); + MethodDescription isCandidate = getters.get( "is" + capitalizedFieldName ); + + if ( getCandidate != null ) { + if ( isCandidate != null ) { + throw new MappingException( + String.format( + Locale.ROOT, + "In trying to locate getter for property [%s], Class [%s] defined " + + "both a `get` [%s] and `is` [%s] variant", + fieldDescription.getName(), + fieldDescription.getDeclaringType().getActualName(), + getCandidate.getActualName(), + isCandidate.getActualName() ) ); + } + + return Optional.of( getCandidate ); + } + + return Optional.ofNullable( isCandidate ); + } } 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 992bf8d84b..cc8762ef49 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 @@ -15,12 +15,18 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.Transient; +import org.hibernate.MappingException; import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker; import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker; import org.hibernate.bytecode.enhance.spi.CollectionTracker; @@ -51,7 +57,6 @@ import net.bytebuddy.description.annotation.AnnotationList; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldDescription.InDefinedShape; import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.modifier.FieldPersistence; import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.type.TypeDefinition; @@ -390,7 +395,7 @@ public class EnhancerImpl implements Enhancer { if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) { continue; } - AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( ctField ); + AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( enhancementContext, ctField ); if ( enhancementContext.isPersistentField( annotatedField ) && !enhancementContext.isMappedCollection( annotatedField ) ) { if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) { collectionList.add( annotatedField ); @@ -420,7 +425,7 @@ public class EnhancerImpl implements Enhancer { for ( FieldDescription ctField : managedCtSuperclass.getDeclaredFields() ) { if ( !Modifier.isStatic( ctField.getModifiers() ) ) { - AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( ctField ); + AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( enhancementContext, ctField ); if ( enhancementContext.isPersistentField( annotatedField ) && !enhancementContext.isMappedCollection( annotatedField ) ) { if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) { collectionList.add( annotatedField ); @@ -436,30 +441,18 @@ public class EnhancerImpl implements Enhancer { return Character.toUpperCase( value.charAt( 0 ) ) + value.substring( 1 ); } - static MethodDescription getterOf(AnnotatedFieldDescription persistentField) { - return getterOf( persistentField.fieldDescription ); - } - - static MethodDescription getterOf(FieldDescription persistentField) { - MethodList methodList = MethodGraph.Compiler.DEFAULT.compile( persistentField.getDeclaringType().asErasure() ) - .listNodes() - .asMethodList() - .filter( isGetter( persistentField.getName() ) ); - if ( methodList.size() == 1 ) { - return methodList.getOnly(); - } - else { - return null; - } - } - static class AnnotatedFieldDescription implements UnloadedField { + private final ByteBuddyEnhancementContext context; + private final FieldDescription fieldDescription; private AnnotationList annotations; - AnnotatedFieldDescription(FieldDescription fieldDescription) { + private Optional getter; + + AnnotatedFieldDescription(ByteBuddyEnhancementContext context, FieldDescription fieldDescription) { + this.context = context; this.fieldDescription = fieldDescription; } @@ -500,35 +493,43 @@ public class EnhancerImpl implements Enhancer { return fieldDescription; } + Optional getGetter() { + if ( getter == null ) { + getter = context.resolveGetter( fieldDescription ); + } + + return getter; + } + private AnnotationList getAnnotations() { if ( annotations == null ) { - annotations = doGetAnnotations( fieldDescription ); + annotations = doGetAnnotations(); } return annotations; } - private static AnnotationList doGetAnnotations(FieldDescription fieldDescription) { + private AnnotationList doGetAnnotations() { AnnotationDescription.Loadable access = fieldDescription.getDeclaringType().asErasure() .getDeclaredAnnotations().ofType( Access.class ); if ( access != null && access.loadSilent().value() == AccessType.PROPERTY ) { - MethodDescription getter = getterOf( fieldDescription ); - if ( getter == null ) { - return fieldDescription.getDeclaredAnnotations(); + Optional getter = getGetter(); + if ( getter.isPresent() ) { + return getter.get().getDeclaredAnnotations(); } else { - return getter.getDeclaredAnnotations(); + return fieldDescription.getDeclaredAnnotations(); } } else if ( access != null && access.loadSilent().value() == AccessType.FIELD ) { return fieldDescription.getDeclaredAnnotations(); } else { - MethodDescription getter = getterOf( fieldDescription ); + Optional getter = getGetter(); // Note that the order here is important List annotationDescriptions = new ArrayList<>(); - if ( getter != null ) { - annotationDescriptions.addAll( getter.getDeclaredAnnotations() ); + if ( getter.isPresent() ) { + annotationDescriptions.addAll( getter.get().getDeclaredAnnotations() ); } annotationDescriptions.addAll( fieldDescription.getDeclaredAnnotations() ); diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/FieldAccessEnhancer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/FieldAccessEnhancer.java index d5f2020ca7..2bde18a1a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/FieldAccessEnhancer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/FieldAccessEnhancer.java @@ -129,7 +129,7 @@ final class FieldAccessEnhancer implements AsmVisitorWrapper.ForDeclaredMethods. ); throw new EnhancementException( msg ); } - return new AnnotatedFieldDescription( fields.getOnly() ); + return new AnnotatedFieldDescription( enhancementContext, fields.getOnly() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java index 715ceef90a..b285a9200e 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java @@ -74,7 +74,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) { continue; } - AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( ctField ); + AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( enhancementContext, ctField ); if ( !ctField.isStatic() && enhancementContext.isPersistentField( annotatedField ) ) { persistentFieldList.add( annotatedField ); } @@ -110,7 +110,7 @@ final class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDecla if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) { continue; } - AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( ctField ); + AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( enhancementContext, ctField ); if ( !ctField.isStatic() && enhancementContext.isPersistentField( annotatedField ) ) { persistentFieldList.add( annotatedField ); }