1111import jakarta .persistence .Entity ;
1212import jakarta .persistence .Id ;
1313import jakarta .persistence .MappedSuperclass ;
14+ import jakarta .persistence .Transient ;
1415import jakarta .persistence .metamodel .Type ;
1516import net .bytebuddy .asm .Advice ;
1617import net .bytebuddy .description .annotation .AnnotationDescription ;
7273import static net .bytebuddy .matcher .ElementMatchers .isDefaultFinalizer ;
7374import static net .bytebuddy .matcher .ElementMatchers .isGetter ;
7475import 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
7680public 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