DATACMNS-556 - Make annotation validation in AnnotationBasedPersistentProperty more lenient.

Annotations used on both fields and accessors are now only rejected if they're Spring Data annotations (i.e. from the org.springframework.data namespace) and the configurations of these annotations differ.
This commit is contained in:
Oliver Gierke
2014-08-10 13:10:45 +02:00
parent 3660676338
commit 5352701374
2 changed files with 51 additions and 12 deletions

View File

@@ -47,6 +47,8 @@ import org.springframework.util.Assert;
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>> extends
AbstractPersistentProperty<P> {
private static final String SPRING_DATA_PACKAGE = "org.springframework.data";
private final Value value;
private final Map<Class<? extends Annotation>, Annotation> annotationCache = new HashMap<Class<? extends Annotation>, Annotation>();
@@ -91,11 +93,9 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationCache.containsKey(annotationType) && !annotationCache.get(annotationType).equals(annotation)) {
throw new MappingException(String.format("Ambiguous mapping! Annotation %s configured "
+ "multiple times on accessor methods of property %s in class %s!", annotationType.getSimpleName(),
getName(), getOwner().getType().getSimpleName()));
}
validateAnnotation(annotation, "Ambiguous mapping! Annotation %s configured "
+ "multiple times on accessor methods of property %s in class %s!", annotationType.getSimpleName(),
getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType, annotation);
}
@@ -109,16 +109,35 @@ public abstract class AnnotationBasedPersistentProperty<P extends PersistentProp
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationCache.containsKey(annotationType)) {
throw new MappingException(String.format("Ambiguous mapping! Annotation %s configured "
+ "on field %s and one of its accessor methods in class %s!", annotationType.getSimpleName(),
field.getName(), getOwner().getType().getSimpleName()));
}
validateAnnotation(annotation, "Ambiguous mapping! Annotation %s configured "
+ "on field %s and one of its accessor methods in class %s!", annotationType.getSimpleName(),
field.getName(), getOwner().getType().getSimpleName());
annotationCache.put(annotationType, annotation);
}
}
/**
* Verifies the given annotation candidate detected. Will be rejected if it's a Spring Data annotation and we already
* found another one with a different configuration setup (i.e. other attribute values).
*
* @param candidate must not be {@literal null}.
* @param message must not be {@literal null}.
* @param arguments must not be {@literal null}.
*/
private void validateAnnotation(Annotation candidate, String message, Object... arguments) {
Class<? extends Annotation> annotationType = candidate.annotationType();
if (!annotationType.getName().startsWith(SPRING_DATA_PACKAGE)) {
return;
}
if (annotationCache.containsKey(annotationType) && !annotationCache.get(annotationType).equals(candidate)) {
throw new MappingException(String.format(message, arguments));
}
}
/**
* Inspects a potentially available {@link Value} annotation at the property and returns the {@link String} value of
* it.

View File

@@ -25,6 +25,8 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.AccessType;
@@ -156,8 +158,8 @@ public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
/**
* @see DATACMNS-243
*/
@Test(expected = MappingException.class)
public void detectsAmbiguityCausedByFieldAnAccessorAnnotation() {
@Test
public void doesNotRejectSameAnnotationIfItsEqualOnBothFieldAndAccessor() {
context.getPersistentEntity(AnotherInvalidSample.class);
}
@@ -193,6 +195,14 @@ public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
assertThat(getProperty(ClassWithReadOnlyProperties.class, "customReadOnlyProperty").isWritable(), is(false));
}
/**
* @see DATACMNS-556
*/
@Test
public void doesNotRejectNonSpringDataAnnotationsUsedOnBothFieldAndAccessor() {
getProperty(TypeWithCustomAnnotationsOnBothFieldAndAccessor.class, "field");
}
@SuppressWarnings("unchecked")
private Map<Class<? extends Annotation>, Annotation> getAnnotationCache(SamplePersistentProperty property) {
return (Map<Class<? extends Annotation>, Annotation>) ReflectionTestUtils.getField(property, "annotationCache");
@@ -317,6 +327,16 @@ public class AnnotationBasedPersistentPropertyUnitTests<P extends AnnotationBase
@CustomReadOnly String customReadOnlyProperty;
}
static class TypeWithCustomAnnotationsOnBothFieldAndAccessor {
@Nullable String field;
@Nullable
public String getField() {
return field;
}
}
@ReadOnlyProperty
@Retention(RetentionPolicy.RUNTIME)
@Target(FIELD)