7575import static org .hibernate .grammars .hql .HqlLexer .ORDER ;
7676import static org .hibernate .grammars .hql .HqlLexer .WHERE ;
7777import static org .hibernate .internal .util .StringHelper .qualify ;
78+ import static org .hibernate .internal .util .StringHelper .unqualify ;
7879import static org .hibernate .processor .annotation .AbstractQueryMethod .isSessionParameter ;
7980import static org .hibernate .processor .annotation .AbstractQueryMethod .isSpecialParam ;
8081import static org .hibernate .processor .annotation .QueryMethod .isOrderParam ;
@@ -966,8 +967,9 @@ private void validateAssociation(Element memberOfClass, AnnotationMirror annotat
966967 final TypeElement assocTypeElement = (TypeElement ) assocDeclaredType .asElement ();
967968 if ( hasAnnotation (assocTypeElement , ENTITY ) ) {
968969 final AnnotationValue mappedBy = getAnnotationValue (annotation , "mappedBy" );
969- final String propertyName = mappedBy == null ? null : mappedBy .getValue ().toString ();
970- validateBidirectionalMapping (memberOfClass , annotation , propertyName , assocTypeElement );
970+ if ( mappedBy != null ) {
971+ validateBidirectionalMapping (memberOfClass , annotation , mappedBy , assocTypeElement );
972+ }
971973 }
972974 else {
973975 message (memberOfClass , "type '" + assocTypeElement .getSimpleName ()
@@ -983,30 +985,27 @@ private void validateAssociation(Element memberOfClass, AnnotationMirror annotat
983985 }
984986
985987 private void validateBidirectionalMapping (
986- Element memberOfClass , AnnotationMirror annotation , @ Nullable String mappedBy , TypeElement assocTypeElement ) {
987- if ( mappedBy != null && ! mappedBy . isEmpty () ) {
988- if ( mappedBy . equals ( "<error>" ) ) {
989- return ;
990- // throw new ProcessLaterException();
991- }
992- if ( mappedBy .indexOf ('.' )> 0 ) {
988+ Element memberOfClass , AnnotationMirror annotation , AnnotationValue annotationVal , TypeElement assocTypeElement ) {
989+ final String mappedBy = annotationVal . getValue (). toString ();
990+ if ( mappedBy != null && ! mappedBy . isEmpty ()
991+ // this happens for a typesafe ref, e.g. Page_BOOK
992+ // TODO: we should queue it to validate it later somehow
993+ && ! mappedBy . equals ( "<error>" ) ) {
994+ if ( mappedBy .indexOf ( '.' ) > 0 ) {
993995 //we don't know how to handle paths yet
994996 return ;
995997 }
996- final AnnotationValue annotationVal =
997- castNonNull (getAnnotationValue (annotation , "mappedBy" ));
998- for ( Element member : context .getAllMembers (assocTypeElement ) ) {
999- if ( propertyName (this , member ).contentEquals (mappedBy )
1000- && compatibleAccess (assocTypeElement , member ) ) {
1001- validateBackRef (memberOfClass , annotation , assocTypeElement , member , annotationVal );
998+ for ( Element member : context .getAllMembers ( assocTypeElement ) ) {
999+ if ( propertyName ( this , member ).contentEquals ( mappedBy )
1000+ && compatibleAccess ( assocTypeElement , member ) ) {
1001+ validateBackRef ( memberOfClass , annotation , assocTypeElement , member , annotationVal );
10021002 return ;
10031003 }
10041004 }
10051005 // not found
1006- message (memberOfClass , annotation ,
1007- annotationVal ,
1006+ message ( memberOfClass , annotation , annotationVal ,
10081007 "no matching member in '" + assocTypeElement .getSimpleName () + "'" ,
1009- Diagnostic .Kind .ERROR );
1008+ Diagnostic .Kind .ERROR );
10101009 }
10111010 }
10121011
@@ -1024,51 +1023,62 @@ private void validateBackRef(
10241023 Element memberOfClass ,
10251024 AnnotationMirror annotation ,
10261025 TypeElement assocTypeElement ,
1027- Element member ,
1026+ Element referencedMember ,
10281027 AnnotationValue annotationVal ) {
10291028 final TypeMirror backType ;
1029+ final String expectedMappingAnnotation ;
10301030 switch ( annotation .getAnnotationType ().asElement ().toString () ) {
10311031 case ONE_TO_ONE :
1032- backType = attributeType (member );
1033- if ( !hasAnnotation (member , ONE_TO_ONE ) ) {
1034- message (memberOfClass , annotation , annotationVal ,
1035- "member '" + member .getSimpleName ()
1036- + "' of '" + assocTypeElement .getSimpleName ()
1037- + "' is not annotated '@OneToOne'" ,
1038- Diagnostic .Kind .WARNING );
1039- }
1032+ backType = attributeType (referencedMember );
1033+ expectedMappingAnnotation = ONE_TO_ONE ;
10401034 break ;
10411035 case ONE_TO_MANY :
1042- backType = attributeType (member );
1043- if ( !hasAnnotation (member , MANY_TO_ONE ) ) {
1044- message (memberOfClass , annotation , annotationVal ,
1045- "member '" + member .getSimpleName ()
1046- + "' of '" + assocTypeElement .getSimpleName ()
1047- + "' is not annotated '@ManyToOne'" ,
1048- Diagnostic .Kind .WARNING );
1049- }
1036+ backType = attributeType (referencedMember );
1037+ expectedMappingAnnotation = MANY_TO_ONE ;
10501038 break ;
10511039 case MANY_TO_MANY :
1052- backType = elementType ( attributeType (member ) );
1053- if ( !hasAnnotation (member , MANY_TO_MANY ) ) {
1040+ backType = elementType ( attributeType (referencedMember ) );
1041+ expectedMappingAnnotation = MANY_TO_MANY ;
1042+ break ;
1043+ default :
1044+ throw new AssertionFailure ("should not have a mappedBy" );
1045+ }
1046+ if ( backType != null ) {
1047+ final Element idMember = getIdMember ();
1048+ final Types typeUtils = context .getTypeUtils ();
1049+ if ( idMember != null && typeUtils .isSameType ( backType , idMember .asType () ) ) {
1050+ // mappedBy references a regular field of the same type as the entity id
1051+ //TODO: any other validation to do here??
1052+ }
1053+ else if ( typeUtils .isSameType ( backType , element .asType () ) ) {
1054+ // mappedBy references a field of the same type as the entity
1055+ // it needs to be mapped as the appropriate sort of association
1056+ if ( !hasAnnotation ( referencedMember , expectedMappingAnnotation ) ) {
10541057 message (memberOfClass , annotation , annotationVal ,
1055- "member '" + member .getSimpleName ()
1058+ "member '" + referencedMember .getSimpleName ()
10561059 + "' of '" + assocTypeElement .getSimpleName ()
1057- + "' is not annotated '@ManyToMany '" ,
1060+ + "' is not annotated '@" + unqualify ( expectedMappingAnnotation ) + " '" ,
10581061 Diagnostic .Kind .WARNING );
10591062 }
1060- break ;
1061- default :
1062- throw new AssertionFailure ("should not have a mappedBy" );
1063+ }
1064+ else {
1065+ // mappedBy references a field which seems to be of the wrong type
1066+ message ( memberOfClass , annotation , annotationVal ,
1067+ "member '" + referencedMember .getSimpleName ()
1068+ + "' of '" + assocTypeElement .getSimpleName ()
1069+ + "' is not of type '" + element .getSimpleName () + "'" ,
1070+ Diagnostic .Kind .WARNING );
1071+ }
10631072 }
1064- if ( backType != null
1065- && ! context . getTypeUtils (). isSameType ( backType , element . asType ()) ) {
1066- message ( memberOfClass , annotation , annotationVal ,
1067- "member '" + member . getSimpleName ()
1068- + "' of '" + assocTypeElement . getSimpleName ()
1069- + "' is not of type '" + element . getSimpleName () + "'" ,
1070- Diagnostic . Kind . WARNING );
1073+ }
1074+
1075+ private @ Nullable Element getIdMember () {
1076+ for ( Element e : element . getEnclosedElements () ) {
1077+ if ( hasAnnotation ( e , ID , EMBEDDED_ID ) ) {
1078+ return e ;
1079+ }
10711080 }
1081+ return null ;
10721082 }
10731083
10741084 private boolean isPersistent (Element memberOfClass , AccessType membersKind ) {
0 commit comments