6
6
*/
7
7
package org .hibernate .bytecode .enhance .internal .bytebuddy ;
8
8
9
- import java .lang .annotation .Annotation ;
10
- import java .lang .reflect .Modifier ;
11
- import java .util .ArrayList ;
12
- import java .util .Collection ;
13
- import java .util .Collections ;
14
- import java .util .List ;
15
- import java .util .Map ;
16
- import java .util .Objects ;
17
- import java .util .Optional ;
18
- import java .util .function .Supplier ;
19
-
9
+ import jakarta .persistence .Access ;
10
+ import jakarta .persistence .AccessType ;
11
+ import jakarta .persistence .metamodel .Type ;
12
+ import net .bytebuddy .asm .Advice ;
13
+ import net .bytebuddy .description .annotation .AnnotationDescription ;
14
+ import net .bytebuddy .description .annotation .AnnotationList ;
15
+ import net .bytebuddy .description .field .FieldDescription ;
16
+ import net .bytebuddy .description .field .FieldDescription .InDefinedShape ;
17
+ import net .bytebuddy .description .method .MethodDescription ;
18
+ import net .bytebuddy .description .type .TypeDefinition ;
19
+ import net .bytebuddy .description .type .TypeDescription ;
20
+ import net .bytebuddy .description .type .TypeDescription .Generic ;
21
+ import net .bytebuddy .description .type .TypeList ;
22
+ import net .bytebuddy .dynamic .DynamicType ;
23
+ import net .bytebuddy .dynamic .scaffold .MethodGraph ;
24
+ import net .bytebuddy .implementation .FieldAccessor ;
25
+ import net .bytebuddy .implementation .FixedValue ;
26
+ import net .bytebuddy .implementation .Implementation ;
27
+ import net .bytebuddy .implementation .StubMethod ;
20
28
import org .hibernate .Version ;
21
29
import org .hibernate .bytecode .enhance .VersionMismatchException ;
22
30
import org .hibernate .bytecode .enhance .internal .tracker .CompositeOwnerTracker ;
41
49
import org .hibernate .internal .CoreLogging ;
42
50
import org .hibernate .internal .CoreMessageLogger ;
43
51
44
- import jakarta .persistence .Access ;
45
- import jakarta .persistence .AccessType ;
46
- import jakarta .persistence .metamodel .Type ;
47
- import net .bytebuddy .asm .Advice ;
48
- import net .bytebuddy .description .annotation .AnnotationDescription ;
49
- import net .bytebuddy .description .annotation .AnnotationList ;
50
- import net .bytebuddy .description .field .FieldDescription ;
51
- import net .bytebuddy .description .field .FieldDescription .InDefinedShape ;
52
- import net .bytebuddy .description .method .MethodDescription ;
53
- import net .bytebuddy .description .type .TypeDefinition ;
54
- import net .bytebuddy .description .type .TypeDescription ;
55
- import net .bytebuddy .description .type .TypeDescription .Generic ;
56
- import net .bytebuddy .dynamic .DynamicType ;
57
- import net .bytebuddy .implementation .FieldAccessor ;
58
- import net .bytebuddy .implementation .FixedValue ;
59
- import net .bytebuddy .implementation .Implementation ;
60
- import net .bytebuddy .implementation .StubMethod ;
52
+ import java .lang .annotation .Annotation ;
53
+ import java .lang .reflect .Modifier ;
54
+ import java .util .ArrayList ;
55
+ import java .util .Collection ;
56
+ import java .util .Collections ;
57
+ import java .util .List ;
58
+ import java .util .Map ;
59
+ import java .util .Objects ;
60
+ import java .util .Optional ;
61
+ import java .util .function .Supplier ;
61
62
62
63
import static net .bytebuddy .matcher .ElementMatchers .isDefaultFinalizer ;
63
64
@@ -172,6 +173,11 @@ private DynamicType.Builder<?> doEnhance(Supplier<DynamicType.Builder<?>> builde
172
173
}
173
174
174
175
if ( enhancementContext .isEntityClass ( managedCtClass ) ) {
176
+ if ( hasUnsupportedAttributeNaming ( managedCtClass ) ) {
177
+ // do not enhance classes with mismatched names for PROPERTY-access persistent attributes
178
+ return null ;
179
+ }
180
+
175
181
log .debugf ( "Enhancing [%s] as Entity" , managedCtClass .getName () );
176
182
DynamicType .Builder <?> builder = builderSupplier .get ();
177
183
builder = builder .implement ( ManagedEntity .class )
@@ -331,6 +337,11 @@ private DynamicType.Builder<?> doEnhance(Supplier<DynamicType.Builder<?>> builde
331
337
return createTransformer ( managedCtClass ).applyTo ( builder );
332
338
}
333
339
else if ( enhancementContext .isCompositeClass ( managedCtClass ) ) {
340
+ if ( hasUnsupportedAttributeNaming ( managedCtClass ) ) {
341
+ // do not enhance classes with mismatched names for PROPERTY-access persistent attributes
342
+ return null ;
343
+ }
344
+
334
345
log .debugf ( "Enhancing [%s] as Composite" , managedCtClass .getName () );
335
346
336
347
DynamicType .Builder <?> builder = builderSupplier .get ();
@@ -364,6 +375,12 @@ else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
364
375
return createTransformer ( managedCtClass ).applyTo ( builder );
365
376
}
366
377
else if ( enhancementContext .isMappedSuperclassClass ( managedCtClass ) ) {
378
+
379
+ // Check for HHH-16572 (PROPERTY attributes with mismatched field and method names)
380
+ if ( hasUnsupportedAttributeNaming ( managedCtClass ) ) {
381
+ return null ;
382
+ }
383
+
367
384
log .debugf ( "Enhancing [%s] as MappedSuperclass" , managedCtClass .getName () );
368
385
369
386
DynamicType .Builder <?> builder = builderSupplier .get ();
@@ -380,6 +397,82 @@ else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
380
397
}
381
398
}
382
399
400
+ /**
401
+ * Check whether an entity class ({@code managedCtClass}) has mismatched names between a persistent field and its
402
+ * getter/setter when using {@link AccessType#PROPERTY}, which Hibernate does not currently support for enhancement.
403
+ * See https://hibernate.atlassian.net/browse/HHH-16572
404
+ */
405
+ private boolean hasUnsupportedAttributeNaming (TypeDescription managedCtClass ) {
406
+ // For process access rules, See https://jakarta.ee/specifications/persistence/3.2/jakarta-persistence-spec-3.2#default-access-type
407
+ // and https://jakarta.ee/specifications/persistence/3.2/jakarta-persistence-spec-3.2#a122
408
+ //
409
+ // This check will determine if entity field names do not match Property accessor method name
410
+ // For example:
411
+ // @Entity
412
+ // class Book {
413
+ // Integer id;
414
+ // String smtg;
415
+ //
416
+ // @Id Integer getId() { return id; }
417
+ // String getSomething() { return smtg; }
418
+ // }
419
+ //
420
+ // Check name of the getter/setter method with persistence annotation and getter/setter method name that doesn't refer to an entity field
421
+ // and will return false. If the property accessor method(s) are named to match the field name(s), return true.
422
+ boolean propertyHasAnnotation = false ;
423
+ MethodGraph .Linked methodGraph = MethodGraph .Compiler .Default .forJavaHierarchy ().compile ((TypeDefinition ) managedCtClass );
424
+ for (MethodGraph .Node node : methodGraph .listNodes ()) {
425
+ MethodDescription methodDescription = node .getRepresentative ();
426
+ if (methodDescription .getDeclaringType ().represents (Object .class )) { // skip class java.lang.Object methods
427
+ continue ;
428
+ }
429
+
430
+ String methodName = methodDescription .getActualName ();
431
+ if (methodName .equals ("" ) ||
432
+ (!methodName .startsWith ("get" ) && !methodName .startsWith ("set" ) && !methodName .startsWith ("is" ))) {
433
+ continue ;
434
+ }
435
+ String methodFieldName ;
436
+ if (methodName .startsWith ("is" )) { // skip past "is"
437
+ methodFieldName = methodName .substring (2 );
438
+ }
439
+ else if (methodName .startsWith ("get" ) ||
440
+ methodName .startsWith ("set" )) { // skip past "get" or "set"
441
+ methodFieldName = methodName .substring (3 );
442
+ }
443
+ else {
444
+ // not a property accessor method so ignore it
445
+ continue ;
446
+ }
447
+ boolean propertyNameMatchesFieldName = false ;
448
+ // convert field letter to lower case
449
+ methodFieldName = methodFieldName .substring (0 , 1 ).toLowerCase () + methodFieldName .substring (1 );
450
+ TypeList typeList = methodDescription .getDeclaredAnnotations ().asTypeList ();
451
+ if (typeList .stream ().anyMatch (typeDefinitions ->
452
+ (typeDefinitions .getName ().contains ("jakarta.persistence" )))) {
453
+ propertyHasAnnotation = true ;
454
+ }
455
+ for (FieldDescription ctField : methodDescription .getDeclaringType ().getDeclaredFields ()) {
456
+ if (!Modifier .isStatic (ctField .getModifiers ())) {
457
+ AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription (enhancementContext , ctField );
458
+ boolean containsPropertyAccessorMethods = false ;
459
+ if (enhancementContext .isPersistentField (annotatedField )) {
460
+ if (methodFieldName .equals (ctField .getActualName ())) {
461
+ propertyNameMatchesFieldName = true ;
462
+ break ;
463
+ }
464
+ }
465
+ }
466
+ }
467
+ if (propertyHasAnnotation && !propertyNameMatchesFieldName ) {
468
+ log .debugf ("Skipping enhancement of [%s]: due to class [%s] not having a property accessor method name matching field name [%s]" ,
469
+ managedCtClass , methodDescription .getDeclaringType ().getActualName (), methodFieldName );
470
+ return true ;
471
+ }
472
+ }
473
+ return false ;
474
+ }
475
+
383
476
private static void verifyVersions (TypeDescription managedCtClass , ByteBuddyEnhancementContext enhancementContext ) {
384
477
final AnnotationDescription .Loadable <EnhancementInfo > existingInfo = managedCtClass
385
478
.getDeclaredAnnotations ()
0 commit comments