6
6
7
7
import java .lang .annotation .Annotation ;
8
8
import java .lang .invoke .MethodHandles ;
9
+ import java .util .Collection ;
9
10
import java .util .EnumSet ;
10
11
import java .util .List ;
11
12
import java .util .Map ;
12
13
14
+ import jakarta .persistence .MapKey ;
15
+ import jakarta .persistence .MapKeyClass ;
16
+ import jakarta .persistence .MapKeyColumn ;
17
+ import jakarta .persistence .MapKeyEnumerated ;
18
+ import jakarta .persistence .MapKeyJoinColumn ;
19
+ import jakarta .persistence .MapKeyJoinColumns ;
20
+ import jakarta .persistence .MapKeyTemporal ;
21
+ import jakarta .persistence .OrderBy ;
22
+ import jakarta .persistence .OrderColumn ;
13
23
import org .hibernate .AnnotationException ;
14
24
import org .hibernate .AssertionFailure ;
15
25
import org .hibernate .MappingException ;
34
44
import org .hibernate .generator .EventType ;
35
45
import org .hibernate .generator .EventTypeSets ;
36
46
import org .hibernate .internal .CoreMessageLogger ;
37
- import org .hibernate .internal .util .collections .CollectionHelper ;
38
- import org .hibernate .mapping .Collection ;
39
47
import org .hibernate .mapping .Component ;
40
48
import org .hibernate .mapping .Join ;
41
49
import org .hibernate .mapping .KeyValue ;
49
57
import org .hibernate .metamodel .spi .EmbeddableInstantiator ;
50
58
import org .hibernate .models .spi .AnnotationDescriptor ;
51
59
import org .hibernate .models .spi .AnnotationDescriptorRegistry ;
60
+ import org .hibernate .models .spi .ArrayTypeDetails ;
52
61
import org .hibernate .models .spi .ClassDetails ;
53
62
import org .hibernate .models .spi .MemberDetails ;
54
63
import org .hibernate .models .spi .SourceModelBuildingContext ;
93
102
import static org .hibernate .boot .model .internal .ToOneBinder .bindOneToOne ;
94
103
import static org .hibernate .id .IdentifierGeneratorHelper .getForeignId ;
95
104
import static org .hibernate .internal .util .StringHelper .qualify ;
105
+ import static org .hibernate .internal .util .collections .CollectionHelper .isEmpty ;
96
106
97
107
/**
98
108
* A stateful binder responsible for creating {@link Property} objects.
@@ -275,29 +285,29 @@ private Property makePropertyAndValue() {
275
285
276
286
@ SuppressWarnings ({"rawtypes" , "unchecked" })
277
287
private void callAttributeBinders (Property property , Map <String , PersistentClass > persistentClasses ) {
278
- final List <? extends Annotation > metaAnnotatedTargets = memberDetails . getMetaAnnotated (
279
- AttributeBinderType .class ,
280
- getSourceModelContext ()
281
- );
282
-
283
- if ( CollectionHelper . isEmpty ( metaAnnotatedTargets ) ) {
284
- return ;
285
- }
286
-
287
- final AnnotationDescriptorRegistry descriptorRegistry = getSourceModelContext (). getAnnotationDescriptorRegistry ( );
288
- for ( int i = 0 ; i < metaAnnotatedTargets . size (); i ++ ) {
289
- final Annotation metaAnnotatedTarget = metaAnnotatedTargets . get ( i );
290
- final AnnotationDescriptor <? extends Annotation > metaAnnotatedDescriptor = descriptorRegistry . getDescriptor ( metaAnnotatedTarget . annotationType () );
291
- final AttributeBinderType binderTypeAnn = metaAnnotatedDescriptor . getDirectAnnotationUsage ( AttributeBinderType . class );
292
- try {
293
- final AttributeBinder binder = binderTypeAnn . binder (). getConstructor (). newInstance ();
294
- final PersistentClass persistentClass = entityBinder != null
295
- ? entityBinder . getPersistentClass ()
296
- : persistentClasses . get ( holder . getEntityName () );
297
- binder . bind ( metaAnnotatedTarget , buildingContext , persistentClass , property );
298
- }
299
- catch ( Exception e ) {
300
- throw new AnnotationException ( "error processing @AttributeBinderType annotation '" + metaAnnotatedDescriptor . getAnnotationType (). getName () + "'" , e );
288
+ final List <? extends Annotation > metaAnnotatedTargets =
289
+ memberDetails . getMetaAnnotated ( AttributeBinderType .class , getSourceModelContext () );
290
+
291
+ if ( ! isEmpty ( metaAnnotatedTargets ) ) {
292
+ final AnnotationDescriptorRegistry descriptorRegistry =
293
+ getSourceModelContext (). getAnnotationDescriptorRegistry ();
294
+ for ( int i = 0 ; i < metaAnnotatedTargets . size (); i ++ ) {
295
+ final Annotation metaAnnotatedTarget = metaAnnotatedTargets . get ( i );
296
+ final AnnotationDescriptor <? extends Annotation > metaAnnotatedDescriptor =
297
+ descriptorRegistry . getDescriptor ( metaAnnotatedTarget . annotationType () );
298
+ final AttributeBinderType binderTypeAnn =
299
+ metaAnnotatedDescriptor . getDirectAnnotationUsage ( AttributeBinderType . class );
300
+ try {
301
+ final AttributeBinder binder = binderTypeAnn . binder (). getConstructor (). newInstance ( );
302
+ final PersistentClass persistentClass = entityBinder != null
303
+ ? entityBinder . getPersistentClass ()
304
+ : persistentClasses . get ( holder . getEntityName () );
305
+ binder . bind ( metaAnnotatedTarget , buildingContext , persistentClass , property );
306
+ }
307
+ catch ( Exception e ) {
308
+ throw new AnnotationException ( "error processing @AttributeBinderType annotation '" +
309
+ metaAnnotatedDescriptor . getAnnotationType (). getName () + "'" , e );
310
+ }
301
311
}
302
312
}
303
313
}
@@ -398,12 +408,14 @@ private Component getOrCreateCompositeId(RootClass rootClass) {
398
408
private Class <? extends EmbeddableInstantiator > resolveCustomInstantiator (
399
409
MemberDetails property ,
400
410
ClassDetails embeddableClass ) {
401
- final org .hibernate .annotations .EmbeddableInstantiator onEmbedded = property .getDirectAnnotationUsage ( org .hibernate .annotations .EmbeddableInstantiator .class );
411
+ final org .hibernate .annotations .EmbeddableInstantiator onEmbedded =
412
+ property .getDirectAnnotationUsage ( org .hibernate .annotations .EmbeddableInstantiator .class );
402
413
if ( onEmbedded != null ) {
403
414
return onEmbedded .value ();
404
415
}
405
416
406
- final org .hibernate .annotations .EmbeddableInstantiator onEmbeddable = embeddableClass .getDirectAnnotationUsage ( org .hibernate .annotations .EmbeddableInstantiator .class );
417
+ final org .hibernate .annotations .EmbeddableInstantiator onEmbeddable =
418
+ embeddableClass .getDirectAnnotationUsage ( org .hibernate .annotations .EmbeddableInstantiator .class );
407
419
if ( onEmbeddable != null ) {
408
420
return onEmbeddable .value ();
409
421
}
@@ -414,6 +426,7 @@ private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(
414
426
//used when the value is provided and the binding is done elsewhere
415
427
public Property makeProperty () {
416
428
validateMake ();
429
+ validateAnnotationsAgainstType ();
417
430
LOG .debugf ( "Building property %s" , name );
418
431
Property property = new Property ();
419
432
property .setName ( name );
@@ -505,7 +518,7 @@ private void handleNaturalId(Property property) {
505
518
506
519
private void inferOptimisticLocking (Property property ) {
507
520
// this is already handled for collections in CollectionBinder...
508
- if ( value instanceof Collection collection ) {
521
+ if ( value instanceof org . hibernate . mapping . Collection collection ) {
509
522
property .setOptimisticLocked ( collection .isOptimisticLocked () );
510
523
}
511
524
else if ( memberDetails != null && memberDetails .hasDirectAnnotationUsage ( OptimisticLock .class ) ) {
@@ -519,6 +532,37 @@ else if ( memberDetails != null && memberDetails.hasDirectAnnotationUsage( Optim
519
532
}
520
533
}
521
534
535
+ private void validateAnnotationsAgainstType () {
536
+ if ( memberDetails != null ) {
537
+ if ( !(memberDetails .getType () instanceof ArrayTypeDetails ) ) {
538
+ checkAnnotation ( OrderColumn .class , List .class );
539
+ if ( memberDetails .hasDirectAnnotationUsage ( OrderBy .class )
540
+ && !memberDetails .getType ().isImplementor ( Collection .class )
541
+ && !memberDetails .getType ().isImplementor ( Map .class ) ) {
542
+ throw new AnnotationException ( "Property '" + qualify ( holder .getPath (), name )
543
+ + "' is annotated '@OrderBy' but is not of type 'Collection' or 'Map'" );
544
+ }
545
+ }
546
+ checkAnnotation ( MapKey .class , Map .class );
547
+ checkAnnotation ( MapKeyColumn .class , Map .class );
548
+ checkAnnotation ( MapKeyClass .class , Map .class );
549
+ checkAnnotation ( MapKeyEnumerated .class , Map .class );
550
+ checkAnnotation ( MapKeyTemporal .class , Map .class );
551
+ checkAnnotation ( MapKeyColumn .class , Map .class );
552
+ checkAnnotation ( MapKeyJoinColumn .class , Map .class );
553
+ checkAnnotation ( MapKeyJoinColumns .class , Map .class );
554
+ }
555
+ }
556
+
557
+ private void checkAnnotation (Class <? extends Annotation > annotationClass , Class <?> propertyType ) {
558
+ if ( memberDetails .hasDirectAnnotationUsage ( annotationClass )
559
+ && !memberDetails .getType ().isImplementor ( propertyType ) ) {
560
+ throw new AnnotationException ( "Property '" + qualify ( holder .getPath (), name )
561
+ + "' is annotated '@" + annotationClass .getSimpleName ()
562
+ + "' but is not of type '" + propertyType .getTypeName () + "'" );
563
+ }
564
+ }
565
+
522
566
private void validateOptimisticLock (boolean excluded ) {
523
567
if ( excluded ) {
524
568
if ( memberDetails .hasDirectAnnotationUsage ( Version .class ) ) {
@@ -587,29 +631,32 @@ private static int addProperty(
587
631
inFlightPropertyDataList .add ( 0 , propertyAnnotatedElement );
588
632
handleIdProperty ( propertyContainer , context , declaringClass , ownerType , element );
589
633
if ( hasToOneAnnotation ( element ) ) {
590
- context .getMetadataCollector ().addToOneAndIdProperty ( ownerType .determineRawClass (), propertyAnnotatedElement );
634
+ context .getMetadataCollector ()
635
+ .addToOneAndIdProperty ( ownerType .determineRawClass (), propertyAnnotatedElement );
591
636
}
592
637
idPropertyCounter ++;
593
638
}
594
639
else {
595
640
inFlightPropertyDataList .add ( propertyAnnotatedElement );
596
641
}
597
642
if ( element .hasDirectAnnotationUsage ( MapsId .class ) ) {
598
- context .getMetadataCollector ().addPropertyAnnotatedWithMapsId ( ownerType .determineRawClass (), propertyAnnotatedElement );
643
+ context .getMetadataCollector ()
644
+ .addPropertyAnnotatedWithMapsId ( ownerType .determineRawClass (), propertyAnnotatedElement );
599
645
}
600
646
601
647
return idPropertyCounter ;
602
648
}
603
649
604
650
private static void checkIdProperty (MemberDetails property , PropertyData propertyData ) {
605
651
final Id incomingIdProperty = property .getDirectAnnotationUsage ( Id .class );
606
- final Id existingIdProperty = propertyData .getAttributeMember ().getDirectAnnotationUsage ( Id .class );
652
+ final MemberDetails attributeMember = propertyData .getAttributeMember ();
653
+ final Id existingIdProperty = attributeMember .getDirectAnnotationUsage ( Id .class );
607
654
if ( incomingIdProperty != null && existingIdProperty == null ) {
608
655
throw new MappingException (
609
656
String .format (
610
657
"You cannot override the [%s] non-identifier property from the [%s] base class or @MappedSuperclass and make it an identifier in the [%s] subclass" ,
611
- propertyData . getAttributeMember () .getName (),
612
- propertyData . getAttributeMember () .getDeclaringType ().getName (),
658
+ attributeMember .getName (),
659
+ attributeMember .getDeclaringType ().getName (),
613
660
property .getDeclaringType ().getName ()
614
661
)
615
662
);
@@ -631,7 +678,8 @@ private static void handleIdProperty(
631
678
if ( element .hasDirectAnnotationUsage ( Id .class ) && element .hasDirectAnnotationUsage ( Column .class ) ) {
632
679
final String columnName = element .getDirectAnnotationUsage ( Column .class ).name ();
633
680
declaringClass .forEachField ( (index , fieldDetails ) -> {
634
- if ( !element .hasDirectAnnotationUsage ( MapsId .class ) && isJoinColumnPresent ( columnName , element , sourceModelContext ) ) {
681
+ if ( !element .hasDirectAnnotationUsage ( MapsId .class )
682
+ && isJoinColumnPresent ( columnName , element , sourceModelContext ) ) {
635
683
//create a PropertyData for the specJ property holding the mapping
636
684
context .getMetadataCollector ().addPropertyAnnotatedWithMapsIdSpecj (
637
685
ownerType .determineRawClass (),
@@ -1157,7 +1205,8 @@ private static boolean isComposite(
1157
1205
MemberDetails property ,
1158
1206
ClassDetails returnedClass ,
1159
1207
PropertyData overridingProperty ) {
1160
- final InheritanceState state = inheritanceStatePerClass .get ( overridingProperty .getClassOrElementType ().determineRawClass () );
1208
+ final InheritanceState state =
1209
+ inheritanceStatePerClass .get ( overridingProperty .getClassOrElementType ().determineRawClass () );
1161
1210
return state != null ? state .hasIdClassOrEmbeddedId () : isEmbedded ( property , returnedClass );
1162
1211
}
1163
1212
0 commit comments