HHH-17875 validate association mappings in processor
This commit is contained in:
parent
c98cd5e675
commit
c9c0261bfa
|
@ -375,6 +375,9 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
putMember( "class", new AnnotationMetaType(this) );
|
putMember( "class", new AnnotationMetaType(this) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validatePersistentMembers( fieldsOfClass );
|
||||||
|
validatePersistentMembers( gettersAndSettersOfClass );
|
||||||
|
|
||||||
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
|
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
|
||||||
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
|
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
|
||||||
}
|
}
|
||||||
|
@ -391,6 +394,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMethods(TypeElement element, List<ExecutableElement> methodsOfClass) {
|
private void addMethods(TypeElement element, List<ExecutableElement> methodsOfClass) {
|
||||||
|
//TODO just use Elements.getAllMembers(element) here!
|
||||||
for ( TypeMirror typeMirror : element.getInterfaces() ) {
|
for ( TypeMirror typeMirror : element.getInterfaces() ) {
|
||||||
final DeclaredType declaredType = (DeclaredType) typeMirror;
|
final DeclaredType declaredType = (DeclaredType) typeMirror;
|
||||||
final TypeElement typeElement = (TypeElement) declaredType.asElement();
|
final TypeElement typeElement = (TypeElement) declaredType.asElement();
|
||||||
|
@ -669,6 +673,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
&& returnType.getKind() != TypeKind.VOID;
|
&& returnType.getKind() != TypeKind.VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validatePersistentMembers(List<? extends Element> membersOfClass) {
|
||||||
|
for ( Element memberOfClass : membersOfClass ) {
|
||||||
|
if ( hasAnnotation(memberOfClass, MANY_TO_ONE, ONE_TO_ONE, ONE_TO_MANY, MANY_TO_MANY) ) {
|
||||||
|
validateAssociation(memberOfClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {
|
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {
|
||||||
for ( Element memberOfClass : membersOfClass ) {
|
for ( Element memberOfClass : membersOfClass ) {
|
||||||
if ( isPersistent( memberOfClass, membersKind ) ) {
|
if ( isPersistent( memberOfClass, membersKind ) ) {
|
||||||
|
@ -692,6 +704,133 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateAssociation(Element memberOfClass) {
|
||||||
|
final TypeMirror type = attributeType(memberOfClass);
|
||||||
|
if ( hasAnnotation(memberOfClass, MANY_TO_ONE) ) {
|
||||||
|
final AnnotationMirror annotation =
|
||||||
|
castNonNull(getAnnotationMirror(memberOfClass, MANY_TO_ONE));
|
||||||
|
validateToOneAssociation(memberOfClass, annotation, type);
|
||||||
|
}
|
||||||
|
else if ( hasAnnotation(memberOfClass, ONE_TO_ONE) ) {
|
||||||
|
final AnnotationMirror annotation =
|
||||||
|
castNonNull(getAnnotationMirror(memberOfClass, ONE_TO_ONE));
|
||||||
|
validateToOneAssociation(memberOfClass, annotation, type);
|
||||||
|
}
|
||||||
|
else if ( hasAnnotation(memberOfClass, ONE_TO_MANY) ) {
|
||||||
|
final AnnotationMirror annotation =
|
||||||
|
castNonNull(getAnnotationMirror(memberOfClass, ONE_TO_MANY));
|
||||||
|
validateToManyAssociation(memberOfClass, annotation, type);
|
||||||
|
}
|
||||||
|
else if ( hasAnnotation(memberOfClass, MANY_TO_MANY) ) {
|
||||||
|
final AnnotationMirror annotation =
|
||||||
|
castNonNull(getAnnotationMirror(memberOfClass, MANY_TO_MANY));
|
||||||
|
validateToManyAssociation(memberOfClass, annotation, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeMirror attributeType(Element memberOfClass) {
|
||||||
|
switch ( memberOfClass.getKind() ) {
|
||||||
|
case METHOD:
|
||||||
|
final ExecutableElement method = (ExecutableElement) memberOfClass;
|
||||||
|
return method.getReturnType();
|
||||||
|
case FIELD:
|
||||||
|
return memberOfClass.asType();
|
||||||
|
default:
|
||||||
|
throw new AssertionFailure("should be a field or getter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateToOneAssociation(Element memberOfClass, AnnotationMirror annotation, TypeMirror type) {
|
||||||
|
final TypeMirror target = (TypeMirror) getAnnotationValue(annotation, "targetEntity");
|
||||||
|
validateAssociation(memberOfClass, annotation, target == null ? type : target);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateToManyAssociation(Element memberOfClass, AnnotationMirror annotation, TypeMirror type) {
|
||||||
|
final TypeMirror target = (TypeMirror) getAnnotationValue(annotation, "targetEntity");
|
||||||
|
validateAssociation(memberOfClass, annotation, target == null ? elementType(type) : target);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateAssociation(Element memberOfClass, AnnotationMirror annotation, @Nullable TypeMirror typeMirror) {
|
||||||
|
if ( typeMirror != null ) {
|
||||||
|
switch ( typeMirror.getKind() ) {
|
||||||
|
case TYPEVAR:
|
||||||
|
if ( hasAnnotation(element, ENTITY) ) {
|
||||||
|
context.message(memberOfClass, "type '" + typeMirror + "' is a type variable",
|
||||||
|
Diagnostic.Kind.WARNING);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DECLARED:
|
||||||
|
final DeclaredType assocDeclaredType = (DeclaredType) typeMirror;
|
||||||
|
final TypeElement assocTypeElement = (TypeElement) assocDeclaredType.asElement();
|
||||||
|
if ( !hasAnnotation(assocTypeElement, ENTITY) ) {
|
||||||
|
context.message(memberOfClass, "type '" + assocTypeElement.getSimpleName()
|
||||||
|
+ "' is not annotated '@Entity'",
|
||||||
|
Diagnostic.Kind.WARNING);
|
||||||
|
}
|
||||||
|
final String mappedBy = (String) getAnnotationValue(annotation, "mappedBy");
|
||||||
|
if ( mappedBy != null && !mappedBy.isEmpty() ) {
|
||||||
|
if ( mappedBy.equals("<error>") ) {
|
||||||
|
return;
|
||||||
|
// throw new ProcessLaterException();
|
||||||
|
}
|
||||||
|
if ( mappedBy.indexOf('.')>0 ) {
|
||||||
|
//we don't know how to handle paths yet
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final List<? extends Element> members =
|
||||||
|
context.getElementUtils().getAllMembers(assocTypeElement);
|
||||||
|
final AnnotationValue annotationVal =
|
||||||
|
castNonNull(getAnnotationValueRef(annotation, "mappedBy"));
|
||||||
|
if ( members.stream().noneMatch(m -> propertyName(this, m).contentEquals(mappedBy)) ) {
|
||||||
|
context.message(memberOfClass, annotation,
|
||||||
|
annotationVal,
|
||||||
|
"no matching member in '" + assocTypeElement.getSimpleName() + "'",
|
||||||
|
Diagnostic.Kind.ERROR);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final Element member =
|
||||||
|
members.stream()
|
||||||
|
.filter(m -> propertyName(this, m).contentEquals(mappedBy))
|
||||||
|
.findFirst().get();
|
||||||
|
if ( hasAnnotation(member, MANY_TO_ONE) ) {
|
||||||
|
final TypeMirror backType = attributeType(member);
|
||||||
|
if ( !context.getTypeUtils().isSameType(backType, element.asType()) ) {
|
||||||
|
context.message(memberOfClass, annotation, annotationVal,
|
||||||
|
"member '" + member.getSimpleName()
|
||||||
|
+ "' of '" + assocTypeElement.getSimpleName()
|
||||||
|
+ "' is not of type '" + element.getSimpleName() + "'",
|
||||||
|
Diagnostic.Kind.WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( hasAnnotation(member, MANY_TO_MANY) ) {
|
||||||
|
final TypeMirror backType = elementType( attributeType(member) );
|
||||||
|
if ( backType != null ) {
|
||||||
|
if ( !context.getTypeUtils().isSameType(backType, element.asType()) ) {
|
||||||
|
context.message(memberOfClass, annotation, annotationVal,
|
||||||
|
"member '" + member.getSimpleName()
|
||||||
|
+ "' of '" + assocTypeElement.getSimpleName()
|
||||||
|
+ "' is not of type '" + element.getSimpleName() + "'",
|
||||||
|
Diagnostic.Kind.WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.message(memberOfClass, annotation, annotationVal,
|
||||||
|
"member '" + member.getSimpleName()
|
||||||
|
+ "' of '" + assocTypeElement.getSimpleName()
|
||||||
|
+ "' is not annotated '@ManyToMany' or '@ManyToOne'",
|
||||||
|
Diagnostic.Kind.WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
context.message(memberOfClass, "type '" + typeMirror + "' is not an entity type",
|
||||||
|
Diagnostic.Kind.WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isPersistent(Element memberOfClass, AccessType membersKind) {
|
private boolean isPersistent(Element memberOfClass, AccessType membersKind) {
|
||||||
return ( entityAccessTypeInfo.getAccessType() == membersKind
|
return ( entityAccessTypeInfo.getAccessType() == membersKind
|
||||||
|| determineAnnotationSpecifiedAccessType( memberOfClass ) != null )
|
|| determineAnnotationSpecifiedAccessType( memberOfClass ) != null )
|
||||||
|
@ -948,6 +1087,27 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @Nullable TypeMirror elementType(TypeMirror parameterType) {
|
||||||
|
switch ( parameterType.getKind() ) {
|
||||||
|
case DECLARED:
|
||||||
|
final DeclaredType declaredType = (DeclaredType) parameterType;
|
||||||
|
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
|
||||||
|
switch ( typeArguments.size() ) {
|
||||||
|
case 1:
|
||||||
|
return typeArguments.get(0);
|
||||||
|
case 2:
|
||||||
|
return typeArguments.get(1);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case ARRAY:
|
||||||
|
final ArrayType arrayType = (ArrayType) parameterType;
|
||||||
|
return arrayType.getComponentType();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String lifecycleOperation(ExecutableElement method) {
|
private static String lifecycleOperation(ExecutableElement method) {
|
||||||
if ( hasAnnotation(method, JD_INSERT) ) {
|
if ( hasAnnotation(method, JD_INSERT) ) {
|
||||||
return "insert";
|
return "insert";
|
||||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.processor.annotation;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.hibernate.processor.Context;
|
import org.hibernate.processor.Context;
|
||||||
import org.hibernate.processor.util.Constants;
|
import org.hibernate.processor.util.Constants;
|
||||||
import org.hibernate.processor.util.TypeUtils;
|
|
||||||
|
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
|
@ -23,6 +22,7 @@ import javax.lang.model.util.Types;
|
||||||
|
|
||||||
import static org.hibernate.processor.util.TypeUtils.getTargetEntity;
|
import static org.hibernate.processor.util.TypeUtils.getTargetEntity;
|
||||||
import static org.hibernate.processor.util.TypeUtils.isBasicAttribute;
|
import static org.hibernate.processor.util.TypeUtils.isBasicAttribute;
|
||||||
|
import static org.hibernate.processor.util.TypeUtils.isPropertyGetter;
|
||||||
import static org.hibernate.processor.util.TypeUtils.toArrayTypeString;
|
import static org.hibernate.processor.util.TypeUtils.toArrayTypeString;
|
||||||
import static org.hibernate.processor.util.TypeUtils.toTypeString;
|
import static org.hibernate.processor.util.TypeUtils.toTypeString;
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable DataAnnotationMetaAttribute visitExecutable(ExecutableType executable, Element element) {
|
public @Nullable DataAnnotationMetaAttribute visitExecutable(ExecutableType executable, Element element) {
|
||||||
return TypeUtils.isPropertyGetter( executable, element )
|
return isPropertyGetter( executable, element )
|
||||||
? executable.getReturnType().accept(this, element)
|
? executable.getReturnType().accept(this, element)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import org.hibernate.processor.Context;
|
||||||
import org.hibernate.processor.util.AccessType;
|
import org.hibernate.processor.util.AccessType;
|
||||||
import org.hibernate.processor.util.AccessTypeInformation;
|
import org.hibernate.processor.util.AccessTypeInformation;
|
||||||
import org.hibernate.processor.util.Constants;
|
import org.hibernate.processor.util.Constants;
|
||||||
import org.hibernate.processor.util.TypeUtils;
|
|
||||||
|
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
|
@ -40,6 +39,7 @@ import static org.hibernate.processor.util.TypeUtils.getAnnotationMirror;
|
||||||
import static org.hibernate.processor.util.TypeUtils.getAnnotationValue;
|
import static org.hibernate.processor.util.TypeUtils.getAnnotationValue;
|
||||||
import static org.hibernate.processor.util.TypeUtils.getCollectionElementType;
|
import static org.hibernate.processor.util.TypeUtils.getCollectionElementType;
|
||||||
import static org.hibernate.processor.util.TypeUtils.getKeyType;
|
import static org.hibernate.processor.util.TypeUtils.getKeyType;
|
||||||
|
import static org.hibernate.processor.util.TypeUtils.getTargetEntity;
|
||||||
import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
|
import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
|
||||||
import static org.hibernate.processor.util.TypeUtils.isBasicAttribute;
|
import static org.hibernate.processor.util.TypeUtils.isBasicAttribute;
|
||||||
import static org.hibernate.processor.util.TypeUtils.isPropertyGetter;
|
import static org.hibernate.processor.util.TypeUtils.isPropertyGetter;
|
||||||
|
@ -86,7 +86,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Nullable
|
||||||
// WARNING: .toString() is necessary here since Name equals does not compare to String
|
// WARNING: .toString() is necessary here since Name equals does not compare to String
|
||||||
final String returnTypeName = returnedElement.getQualifiedName().toString();
|
final String returnTypeName = returnedElement.getQualifiedName().toString();
|
||||||
final String collection = Constants.COLLECTIONS.get( returnTypeName );
|
final String collection = Constants.COLLECTIONS.get( returnTypeName );
|
||||||
final String targetEntity = TypeUtils.getTargetEntity( element.getAnnotationMirrors() );
|
final String targetEntity = getTargetEntity( element.getAnnotationMirrors() );
|
||||||
if ( collection != null ) {
|
if ( collection != null ) {
|
||||||
return createMetaCollectionAttribute( declaredType, element, returnTypeName, collection, targetEntity );
|
return createMetaCollectionAttribute( declaredType, element, returnTypeName, collection, targetEntity );
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Nullable
|
||||||
DeclaredType declaredType, Element element, String returnTypeName, String collection,
|
DeclaredType declaredType, Element element, String returnTypeName, String collection,
|
||||||
@Nullable String targetEntity) {
|
@Nullable String targetEntity) {
|
||||||
if ( hasAnnotation( element, ELEMENT_COLLECTION ) ) {
|
if ( hasAnnotation( element, ELEMENT_COLLECTION ) ) {
|
||||||
final String explicitTargetEntity = TypeUtils.getTargetEntity( element.getAnnotationMirrors() );
|
final String explicitTargetEntity = getTargetEntity( element.getAnnotationMirrors() );
|
||||||
final TypeMirror collectionElementType =
|
final TypeMirror collectionElementType =
|
||||||
getCollectionElementType( declaredType, returnTypeName, explicitTargetEntity, context );
|
getCollectionElementType( declaredType, returnTypeName, explicitTargetEntity, context );
|
||||||
if ( collectionElementType.getKind() == TypeKind.DECLARED ) {
|
if ( collectionElementType.getKind() == TypeKind.DECLARED ) {
|
||||||
|
|
Loading…
Reference in New Issue