Skip to content

Commit f1251ea

Browse files
committed
HHH-19059 Fix check for property access fields on hierarchies
1 parent 4f5d6c4 commit f1251ea

File tree

1 file changed

+48
-56
lines changed
  • hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy

1 file changed

+48
-56
lines changed

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java

+48-56
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import jakarta.persistence.Entity;
1212
import jakarta.persistence.Id;
1313
import jakarta.persistence.MappedSuperclass;
14+
import jakarta.persistence.Transient;
1415
import jakarta.persistence.metamodel.Type;
1516
import net.bytebuddy.asm.Advice;
1617
import net.bytebuddy.description.annotation.AnnotationDescription;
@@ -72,6 +73,9 @@
7273
import static net.bytebuddy.matcher.ElementMatchers.isDefaultFinalizer;
7374
import static net.bytebuddy.matcher.ElementMatchers.isGetter;
7475
import static net.bytebuddy.matcher.ElementMatchers.isSetter;
76+
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
77+
import static net.bytebuddy.matcher.ElementMatchers.named;
78+
import static net.bytebuddy.matcher.ElementMatchers.not;
7579

7680
public class EnhancerImpl implements Enhancer {
7781

@@ -489,21 +493,13 @@ private static boolean hasMappingAnnotation(AnnotationList annotations) {
489493
|| annotations.isAnnotationPresent( Embeddable.class );
490494
}
491495

492-
private static boolean hasPersistenceAnnotation(AnnotationList annotations) {
493-
boolean found = false;
494-
for ( AnnotationDescription annotation : annotations ) {
495-
final String annotationName = annotation.getAnnotationType().getName();
496-
if ( annotationName.startsWith( "jakarta.persistence" ) ) {
497-
if ( annotationName.equals( "jakarta.persistence.Transient" ) ) {
498-
// transient property so ignore it
499-
return false;
500-
}
501-
else if ( !found && !IGNORED_PERSISTENCE_ANNOTATIONS.contains( annotationName ) ) {
502-
found = true;
503-
}
504-
}
496+
private static boolean isPersistentMethod(MethodDescription method) {
497+
final AnnotationList annotations = method.getDeclaredAnnotations();
498+
if ( annotations.isAnnotationPresent( Transient.class ) ) {
499+
return false;
505500
}
506-
return found;
501+
502+
return annotations.stream().noneMatch( a -> IGNORED_PERSISTENCE_ANNOTATIONS.contains( a.getAnnotationType().getName() ) );
507503
}
508504

509505
private static final Set<String> IGNORED_PERSISTENCE_ANNOTATIONS = Set.of(
@@ -516,6 +512,17 @@ else if ( !found && !IGNORED_PERSISTENCE_ANNOTATIONS.contains( annotationName )
516512
"jakarta.persistence.PreUpdate"
517513
);
518514

515+
private static boolean containsField(Generic type, String fieldName) {
516+
do {
517+
if ( !type.getDeclaredFields().filter( not( isStatic() ).and( named( fieldName ) ) ).isEmpty() ) {
518+
return true;
519+
}
520+
type = type.getSuperClass();
521+
}
522+
while ( type != null && !type.represents( Object.class ) );
523+
return false;
524+
}
525+
519526
/**
520527
* Check whether an entity class ({@code managedCtClass}) has mismatched names between a persistent field and its
521528
* getter/setter when using {@link AccessType#PROPERTY}, which Hibernate does not currently support for enhancement.
@@ -558,61 +565,46 @@ private static boolean checkUnsupportedAttributeNaming(TypeDescription managedCt
558565
.asMethodList()
559566
.filter( isGetter().or( isSetter() ) );
560567
for ( final MethodDescription methodDescription : methods ) {
561-
if ( determineAccessType( methodDescription, defaultAccessType ) != AccessType.PROPERTY ) {
568+
if ( methodDescription.getDeclaringType().represents( Object.class )
569+
|| determineAccessType( methodDescription, defaultAccessType ) != AccessType.PROPERTY ) {
562570
// We only need to check this for AccessType.PROPERTY
563571
continue;
564572
}
565573

566574
final String methodName = methodDescription.getActualName();
567-
String methodFieldName;
575+
String fieldName;
568576
if ( methodName.startsWith( "get" ) || methodName.startsWith( "set" ) ) {
569-
methodFieldName = methodName.substring( 3 );
577+
fieldName = methodName.substring( 3 );
570578
}
571579
else {
572580
assert methodName.startsWith( "is" );
573-
methodFieldName = methodName.substring( 2 );
581+
fieldName = methodName.substring( 2 );
574582
}
575583
// convert first field letter to lower case
576-
methodFieldName = getJavaBeansFieldName( methodFieldName );
577-
if ( methodFieldName != null && hasPersistenceAnnotation( methodDescription.getDeclaredAnnotations() ) ) {
578-
boolean propertyNameMatchesFieldName = false;
579-
for ( final FieldDescription field : methodDescription.getDeclaringType().getDeclaredFields() ) {
580-
if ( !Modifier.isStatic( field.getModifiers() ) ) {
581-
final AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription(
582-
enhancementContext,
583-
field
584-
);
585-
if ( enhancementContext.isPersistentField( annotatedField ) ) {
586-
if ( methodFieldName.equals( field.getActualName() ) ) {
587-
propertyNameMatchesFieldName = true;
588-
break;
589-
}
590-
}
591-
}
592-
}
593-
if ( !propertyNameMatchesFieldName ) {
594-
// We shouldn't even be in this method if using LEGACY, see top of this method.
595-
return switch ( strategy ) {
596-
case SKIP -> {
597-
log.debugf(
598-
"Skipping enhancement of [%s] because no field named [%s] could be found for property accessor method [%s]."
599-
+ " To fix this, make sure all property accessor methods have a matching field.",
600-
managedCtClass.getName(),
601-
methodFieldName,
602-
methodDescription.getName()
603-
);
604-
yield true;
605-
}
606-
case FAIL -> throw new EnhancementException( String.format(
607-
"Enhancement of [%s] failed because no field named [%s] could be found for property accessor method [%s]."
608-
+ " To fix this, make sure all property accessor methods have a matching field.",
584+
fieldName = getJavaBeansFieldName( fieldName );
585+
if ( fieldName != null && isPersistentMethod( methodDescription )
586+
&& !containsField( managedCtClass.asGenericType(), fieldName ) ) {
587+
// We shouldn't even be in this method if using LEGACY, see top of this method.
588+
return switch ( strategy ) {
589+
case SKIP -> {
590+
log.debugf(
591+
"Skipping enhancement of [%s] because no field named [%s] could be found for property accessor method [%s]."
592+
+ " To fix this, make sure all property accessor methods have a matching field.",
609593
managedCtClass.getName(),
610-
methodFieldName,
594+
fieldName,
611595
methodDescription.getName()
612-
) );
613-
default -> throw new AssertionFailure( "Unexpected strategy at this point: " + strategy );
614-
};
615-
}
596+
);
597+
yield true;
598+
}
599+
case FAIL -> throw new EnhancementException( String.format(
600+
"Enhancement of [%s] failed because no field named [%s] could be found for property accessor method [%s]."
601+
+ " To fix this, make sure all property accessor methods have a matching field.",
602+
managedCtClass.getName(),
603+
fieldName,
604+
methodDescription.getName()
605+
) );
606+
default -> throw new AssertionFailure( "Unexpected strategy at this point: " + strategy );
607+
};
616608
}
617609
}
618610
return false;

0 commit comments

Comments
 (0)